Skip to content

Commit 4dfb99b

Browse files
authored
Improve class properties code (#187)
***NO_CI***
1 parent 648f335 commit 4dfb99b

File tree

1 file changed

+107
-68
lines changed

1 file changed

+107
-68
lines changed

nanoFramework.System.Text/Text/StringBuilder.cs

Lines changed: 107 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ public sealed class StringBuilder
2121
{
2222
#region Fields
2323

24-
int _maxCapacity;
25-
char[] _chunkChars;
26-
int _chunkLength;
27-
StringBuilder _chunkPrevious;
28-
int _chunkOffset;
24+
private readonly int _maxCapacity;
25+
private char[] _chunkChars;
26+
private int _chunkLength;
27+
private StringBuilder _chunkPrevious;
28+
private int _chunkOffset;
2929

3030
#endregion
3131

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

4640
/// <summary>
4741
/// Gets or sets the character at the specified character position in this instance.
4842
/// </summary>
4943
/// <param name="index">The position of the character.</param>
5044
/// <returns>The Unicode character at position index.</returns>
45+
/// <exception cref="IndexOutOfRangeException"><paramref name="index"/> is outside the bounds of this instance while getting a character.</exception>"
46+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is outside the bounds of this instance while setting a character.</exception>"
5147
public char this[int index]
5248
{
5349
get
5450
{
55-
var chunkPrevious = this;
51+
StringBuilder chunk = this;
52+
5653
while (true)
5754
{
58-
var num = index - chunkPrevious._chunkOffset;
59-
if (num >= 0)
55+
int indexInBlock = index - chunk._chunkOffset;
56+
57+
if (indexInBlock >= 0)
6058
{
61-
if (num >= chunkPrevious._chunkLength)
59+
if (indexInBlock >= chunk._chunkLength)
6260
{
6361
#pragma warning disable S112 // General exceptions should never be thrown
6462
throw new IndexOutOfRangeException();
6563
#pragma warning restore S112 // General exceptions should never be thrown
6664
}
67-
return chunkPrevious._chunkChars[num];
65+
66+
return chunk._chunkChars[indexInBlock];
6867
}
69-
chunkPrevious = chunkPrevious._chunkPrevious;
70-
if (chunkPrevious == null)
68+
69+
chunk = chunk._chunkPrevious;
70+
71+
if (chunk == null)
7172
{
7273
#pragma warning disable S112 // General exceptions should never be thrown
7374
throw new IndexOutOfRangeException();
7475
#pragma warning restore S112 // General exceptions should never be thrown
7576
}
7677
}
7778
}
79+
7880
set
7981
{
80-
var chunkPrevious = this;
81-
Label_0002:
82-
var num = index - chunkPrevious._chunkOffset;
83-
if (num >= 0)
82+
StringBuilder chunk = this;
83+
84+
while (true)
8485
{
85-
if (num >= chunkPrevious._chunkLength)
86+
int indexInBlock = index - chunk._chunkOffset;
87+
88+
if (indexInBlock >= 0)
8689
{
87-
throw new ArgumentOutOfRangeException("index");
90+
if (indexInBlock >= chunk._chunkLength)
91+
{
92+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
93+
throw new ArgumentOutOfRangeException();
94+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
95+
}
96+
97+
chunk._chunkChars[indexInBlock] = value;
98+
return;
8899
}
89-
chunkPrevious._chunkChars[num] = value;
90-
}
91-
else
92-
{
93-
chunkPrevious = chunkPrevious._chunkPrevious;
94-
if (chunkPrevious == null)
100+
101+
chunk = chunk._chunkPrevious;
102+
103+
if (chunk == null)
95104
{
96-
throw new ArgumentOutOfRangeException("index");
105+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
106+
throw new ArgumentOutOfRangeException();
107+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
97108
}
98-
goto Label_0002;
99109
}
100110
}
101111
}
@@ -106,72 +116,101 @@ public char this[int index]
106116
/// <value>
107117
/// 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.
108118
/// </value>
109-
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
119+
/// <exception cref="System.ArgumentOutOfRangeException">
120+
/// <para>The value specified for a set operation is less than the current length of this instance.</para>
121+
/// <para>-or-</para>
122+
/// <para>The value specified for a set operation is greater than the maximum capacity.</para>
123+
/// </exception>
110124
public int Capacity
111125
{
112-
get
113-
{
114-
return _chunkChars.Length + _chunkOffset;
115-
}
126+
get => _chunkChars.Length + _chunkOffset;
127+
116128
set
117129
{
118-
if (value < 0) throw new ArgumentOutOfRangeException("value");
119-
if (value > MaxCapacity) throw new ArgumentOutOfRangeException("value");
120-
if (value < Length) throw new ArgumentOutOfRangeException("value");
130+
if (value < 0 || value > MaxCapacity || value < Length)
131+
{
132+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
133+
throw new ArgumentOutOfRangeException();
134+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
135+
}
136+
121137
if (Capacity != value)
122138
{
123-
var num = value - _chunkOffset;
124-
var destinationArray = new char[num];
139+
int num = value - _chunkOffset;
140+
char[] destinationArray = new char[num];
141+
125142
Array.Copy(_chunkChars, destinationArray, _chunkLength);
143+
126144
_chunkChars = destinationArray;
127145
}
128146
}
129147
}
130148

131149
/// <summary>
132-
/// Gets or sets the length of the current StringBuilder object.
150+
/// Gets or sets the length of the current <see cref="StringBuilder"/> object.
133151
/// </summary>
134152
/// <value>The length of this instance.</value>
135-
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
153+
/// <exception cref="ArgumentOutOfRangeException">If <see cref="Length"/> is set to a value that is less than zero or greater than <see cref="MaxCapacity"/>.</exception>
136154
public int Length
137155
{
138-
get
139-
{
140-
return _chunkOffset + _chunkLength;
141-
}
156+
get => _chunkOffset + _chunkLength;
157+
142158
set
143159
{
144-
if (value < 0) throw new ArgumentOutOfRangeException("value");
145-
if (value > MaxCapacity) throw new ArgumentOutOfRangeException("value");
146-
var capacity = Capacity;
160+
if (value < 0 || value > MaxCapacity)
161+
{
162+
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
163+
throw new ArgumentOutOfRangeException();
164+
#pragma warning restore S3928 // OK to use in .NET nanoFramework context
165+
}
166+
147167
if (value == 0 && _chunkPrevious == null)
148168
{
149169
_chunkLength = 0;
150170
_chunkOffset = 0;
171+
172+
return;
173+
}
174+
175+
int delta = value - Length;
176+
177+
if (delta > 0)
178+
{
179+
// Pad ourselves with null characters.
180+
Append('\0', delta);
151181
}
152182
else
153183
{
154-
var repeatCount = value - Length;
155-
if (repeatCount > 0)
156-
{
157-
Append('\0', repeatCount);
158-
}
159-
else
184+
StringBuilder chunk = FindChunkForIndex(value);
185+
186+
if (chunk != this)
160187
{
161-
var builder = FindChunkForIndex(value);
162-
if (builder != this)
188+
// Avoid possible infinite capacity growth. See https://github.com/dotnet/coreclr/pull/16926
189+
int capacityToPreserve = MathInternal.Min(Capacity, MathInternal.Max(Length * 6 / 5, _chunkChars.Length));
190+
int newLen = capacityToPreserve - chunk._chunkOffset;
191+
192+
if (newLen > chunk._chunkChars.Length)
193+
{
194+
// We crossed a chunk boundary when reducing the Length. We must replace this middle-chunk with a new larger chunk,
195+
// to ensure the capacity we want is preserved.
196+
char[] newArray = new char[newLen];
197+
Array.Copy(chunk._chunkChars, newArray, chunk._chunkLength);
198+
_chunkChars = newArray;
199+
}
200+
else
163201
{
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;
202+
// Special case where the capacity we want to keep corresponds exactly to the size of the content.
203+
// Just take ownership of the array.
204+
_chunkChars = chunk._chunkChars;
170205
}
171-
_chunkLength = value - builder._chunkOffset;
206+
207+
_chunkPrevious = chunk._chunkPrevious;
208+
_chunkOffset = chunk._chunkOffset;
209+
172210
}
173-
}
174211

212+
_chunkLength = value - chunk._chunkOffset;
213+
}
175214
}
176215
}
177216

0 commit comments

Comments
 (0)