Skip to content

Commit 8d6b67f

Browse files
committed
Improve class properties code
- General simplifications. - Add code to Length setter to avoid possible infinite capacity grow (following full .NET fix). - Changed to expression properties. - Remove names from exceptions.
1 parent 648f335 commit 8d6b67f

File tree

1 file changed

+111
-68
lines changed

1 file changed

+111
-68
lines changed

nanoFramework.System.Text/Text/StringBuilder.cs

Lines changed: 111 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ namespace System.Text
1919
/// </remarks>
2020
public sealed class StringBuilder
2121
{
22+
private const int DefaultCapacity = 0x10;
23+
private const int DefaultMaxCapacity = short.MaxValue;
24+
private const int MaxChunkSize = 8000;
25+
2226
#region Fields
2327

24-
int _maxCapacity;
25-
char[] _chunkChars;
26-
int _chunkLength;
27-
StringBuilder _chunkPrevious;
28-
int _chunkOffset;
28+
private readonly int _maxCapacity;
29+
private char[] _chunkChars;
30+
private int _chunkLength;
31+
private StringBuilder _chunkPrevious;
32+
private int _chunkOffset;
2933

3034
#endregion
3135

@@ -35,67 +39,77 @@ public sealed class StringBuilder
3539
/// Gets the maximum capacity of this instance.
3640
/// </summary>
3741
/// <value>The maximum number of characters this instance can hold.</value>
38-
public int MaxCapacity
39-
{
40-
get
41-
{
42-
return _maxCapacity;
43-
}
44-
}
42+
public int MaxCapacity => _maxCapacity;
4543

4644
/// <summary>
4745
/// Gets or sets the character at the specified character position in this instance.
4846
/// </summary>
4947
/// <param name="index">The position of the character.</param>
5048
/// <returns>The Unicode character at position index.</returns>
49+
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is outside the bounds of this instance while getting a character.</exception>"
50+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is outside the bounds of this instance while setting a character.</exception>"
5151
public char this[int index]
5252
{
5353
get
5454
{
55-
var chunkPrevious = this;
55+
StringBuilder chunk = this;
56+
5657
while (true)
5758
{
58-
var num = index - chunkPrevious._chunkOffset;
59-
if (num >= 0)
59+
int indexInBlock = index - chunk._chunkOffset;
60+
61+
if (indexInBlock >= 0)
6062
{
61-
if (num >= chunkPrevious._chunkLength)
63+
if (indexInBlock >= chunk._chunkLength)
6264
{
6365
#pragma warning disable S112 // General exceptions should never be thrown
6466
throw new IndexOutOfRangeException();
6567
#pragma warning restore S112 // General exceptions should never be thrown
6668
}
67-
return chunkPrevious._chunkChars[num];
69+
70+
return chunk._chunkChars[indexInBlock];
6871
}
69-
chunkPrevious = chunkPrevious._chunkPrevious;
70-
if (chunkPrevious == null)
72+
73+
chunk = chunk._chunkPrevious;
74+
75+
if (chunk == null)
7176
{
7277
#pragma warning disable S112 // General exceptions should never be thrown
7378
throw new IndexOutOfRangeException();
7479
#pragma warning restore S112 // General exceptions should never be thrown
7580
}
7681
}
7782
}
83+
7884
set
7985
{
80-
var chunkPrevious = this;
81-
Label_0002:
82-
var num = index - chunkPrevious._chunkOffset;
83-
if (num >= 0)
86+
StringBuilder chunk = this;
87+
88+
while (true)
8489
{
85-
if (num >= chunkPrevious._chunkLength)
90+
int indexInBlock = index - chunk._chunkOffset;
91+
92+
if (indexInBlock >= 0)
8693
{
87-
throw new ArgumentOutOfRangeException("index");
94+
if (indexInBlock >= chunk._chunkLength)
95+
{
96+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
97+
throw new ArgumentOutOfRangeException();
98+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
99+
}
100+
101+
chunk._chunkChars[indexInBlock] = value;
102+
return;
88103
}
89-
chunkPrevious._chunkChars[num] = value;
90-
}
91-
else
92-
{
93-
chunkPrevious = chunkPrevious._chunkPrevious;
94-
if (chunkPrevious == null)
104+
105+
chunk = chunk._chunkPrevious;
106+
107+
if (chunk == null)
95108
{
96-
throw new ArgumentOutOfRangeException("index");
109+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
110+
throw new ArgumentOutOfRangeException();
111+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
97112
}
98-
goto Label_0002;
99113
}
100114
}
101115
}
@@ -106,72 +120,101 @@ public char this[int index]
106120
/// <value>
107121
/// The maximum number of characters that can be contained in the memory allocated by the current instance. Its value can range from Length to MaxCapacity.
108122
/// </value>
109-
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
123+
/// <exception cref="System.ArgumentOutOfRangeException">
124+
/// <para>The value specified for a set operation is less than the current length of this instance.</para>
125+
/// <para>-or-</para>
126+
/// <para>The value specified for a set operation is greater than the maximum capacity.</para>
127+
/// </exception>
110128
public int Capacity
111129
{
112-
get
113-
{
114-
return _chunkChars.Length + _chunkOffset;
115-
}
130+
get => _chunkChars.Length + _chunkOffset;
131+
116132
set
117133
{
118-
if (value < 0) throw new ArgumentOutOfRangeException("value");
119-
if (value > MaxCapacity) throw new ArgumentOutOfRangeException("value");
120-
if (value < Length) throw new ArgumentOutOfRangeException("value");
134+
if (value < 0 || value > MaxCapacity || value < Length)
135+
{
136+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
137+
throw new ArgumentOutOfRangeException();
138+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
139+
}
140+
121141
if (Capacity != value)
122142
{
123-
var num = value - _chunkOffset;
124-
var destinationArray = new char[num];
143+
int num = value - _chunkOffset;
144+
char[] destinationArray = new char[num];
145+
125146
Array.Copy(_chunkChars, destinationArray, _chunkLength);
147+
126148
_chunkChars = destinationArray;
127149
}
128150
}
129151
}
130152

131153
/// <summary>
132-
/// Gets or sets the length of the current StringBuilder object.
154+
/// Gets or sets the length of the current <see cref="StringBuilder"/> object.
133155
/// </summary>
134156
/// <value>The length of this instance.</value>
135-
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
157+
/// <exception cref="ArgumentOutOfRangeException">If <see cref="Length"/> is set to a value that is less than zero or greater than <see cref="MaxCapacity"/>.</exception>
136158
public int Length
137159
{
138-
get
139-
{
140-
return _chunkOffset + _chunkLength;
141-
}
160+
get => _chunkOffset + _chunkLength;
161+
142162
set
143163
{
144-
if (value < 0) throw new ArgumentOutOfRangeException("value");
145-
if (value > MaxCapacity) throw new ArgumentOutOfRangeException("value");
146-
var capacity = Capacity;
164+
if (value < 0 || value > MaxCapacity)
165+
{
166+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
167+
throw new ArgumentOutOfRangeException();
168+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
169+
}
170+
147171
if (value == 0 && _chunkPrevious == null)
148172
{
149173
_chunkLength = 0;
150174
_chunkOffset = 0;
175+
176+
return;
177+
}
178+
179+
int delta = value - Length;
180+
181+
if (delta > 0)
182+
{
183+
// Pad ourselves with null characters.
184+
Append('\0', delta);
151185
}
152186
else
153187
{
154-
var repeatCount = value - Length;
155-
if (repeatCount > 0)
156-
{
157-
Append('\0', repeatCount);
158-
}
159-
else
188+
StringBuilder chunk = FindChunkForIndex(value);
189+
190+
if (chunk != this)
160191
{
161-
var builder = FindChunkForIndex(value);
162-
if (builder != this)
192+
// Avoid possible infinite capacity growth. See https://github.com/dotnet/coreclr/pull/16926
193+
int capacityToPreserve = MathInternal.Min(Capacity, MathInternal.Max(Length * 6 / 5, _chunkChars.Length));
194+
int newLen = capacityToPreserve - chunk._chunkOffset;
195+
196+
if (newLen > chunk._chunkChars.Length)
197+
{
198+
// We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk,
199+
// to ensure the capacity we want is preserved.
200+
char[] newArray = new char[newLen];
201+
Array.Copy(chunk._chunkChars, newArray, chunk._chunkLength);
202+
_chunkChars = newArray;
203+
}
204+
else
163205
{
164-
var num3 = capacity - builder._chunkOffset;
165-
var destinationArray = new char[num3];
166-
Array.Copy(builder._chunkChars, destinationArray, builder._chunkLength);
167-
_chunkChars = destinationArray;
168-
_chunkPrevious = builder._chunkPrevious;
169-
_chunkOffset = builder._chunkOffset;
206+
// Special case where the capacity we want to keep corresponds exactly to the size of the content.
207+
// Just take ownership of the array.
208+
_chunkChars = chunk._chunkChars;
170209
}
171-
_chunkLength = value - builder._chunkOffset;
210+
211+
_chunkPrevious = chunk._chunkPrevious;
212+
_chunkOffset = chunk._chunkOffset;
213+
172214
}
173-
}
174215

216+
_chunkLength = value - chunk._chunkOffset;
217+
}
175218
}
176219
}
177220

0 commit comments

Comments
 (0)