Skip to content

Commit 8d13dd7

Browse files
authored
Merge branch 'master' into string_optimizations
2 parents 5037a5a + 10093b7 commit 8d13dd7

File tree

3 files changed

+354
-93
lines changed

3 files changed

+354
-93
lines changed

QRCoder/QRCodeGenerator.Polynom.cs

+260-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
24
using System.Text;
35

46
namespace QRCoder
@@ -8,21 +10,142 @@ public partial class QRCodeGenerator
810
/// <summary>
911
/// Represents a polynomial, which is a sum of polynomial terms.
1012
/// </summary>
11-
private struct Polynom
13+
private struct Polynom : IDisposable
1214
{
15+
private PolynomItem[] _polyItems;
16+
private int _length;
17+
1318
/// <summary>
1419
/// Initializes a new instance of the <see cref="Polynom"/> struct with a specified number of initial capacity for polynomial terms.
1520
/// </summary>
1621
/// <param name="count">The initial capacity of the polynomial items list.</param>
1722
public Polynom(int count)
1823
{
19-
this.PolyItems = new List<PolynomItem>(count);
24+
_length = 0;
25+
_polyItems = RentArray(count);
26+
}
27+
28+
/// <summary>
29+
/// Adds a polynomial term to the polynomial.
30+
/// </summary>
31+
public void Add(PolynomItem item)
32+
{
33+
AssertCapacity(_length + 1);
34+
_polyItems[_length++] = item;
35+
}
36+
37+
/// <summary>
38+
/// Removes the polynomial term at the specified index.
39+
/// </summary>
40+
public void RemoveAt(int index)
41+
{
42+
if ((uint)index >= (uint)_length)
43+
throw new IndexOutOfRangeException();
44+
45+
if (index < _length - 1)
46+
Array.Copy(_polyItems, index + 1, _polyItems, index, _length - index - 1);
47+
48+
_length--;
49+
}
50+
51+
/// <summary>
52+
/// Gets or sets a polynomial term at the specified index.
53+
/// </summary>
54+
public PolynomItem this[int index]
55+
{
56+
get {
57+
if ((uint)index >= _length)
58+
ThrowIndexOutOfRangeException();
59+
return _polyItems[index];
60+
}
61+
set {
62+
if ((uint)index >= _length)
63+
ThrowIndexOutOfRangeException();
64+
_polyItems[index] = value;
65+
}
66+
}
67+
68+
#if NET6_0_OR_GREATER
69+
[StackTraceHidden]
70+
#endif
71+
private static void ThrowIndexOutOfRangeException()
72+
{
73+
throw new IndexOutOfRangeException();
74+
}
75+
76+
77+
/// <summary>
78+
/// Gets the number of polynomial terms in the polynomial.
79+
/// </summary>
80+
public int Count => _length;
81+
82+
/// <summary>
83+
/// Removes all polynomial terms from the polynomial.
84+
/// </summary>
85+
public void Clear()
86+
{
87+
_length = 0;
2088
}
2189

2290
/// <summary>
23-
/// Gets or sets the list of polynomial items, where each item represents a term in the polynomial.
91+
/// Clones the polynomial, creating a new instance with the same polynomial terms.
2492
/// </summary>
25-
public List<PolynomItem> PolyItems { get; set; }
93+
public Polynom Clone()
94+
{
95+
var newPolynom = new Polynom(_length);
96+
Array.Copy(_polyItems, newPolynom._polyItems, _length);
97+
newPolynom._length = _length;
98+
return newPolynom;
99+
}
100+
101+
/// <summary>
102+
/// Sorts the collection of <see cref="PolynomItem"/> using a custom comparer function.
103+
/// </summary>
104+
/// <param name="comparer">
105+
/// A function that compares two <see cref="PolynomItem"/> objects and returns an integer indicating their relative order:
106+
/// less than zero if the first is less than the second, zero if they are equal, or greater than zero if the first is greater than the second.
107+
/// </param>
108+
public void Sort(Func<PolynomItem, PolynomItem, int> comparer)
109+
{
110+
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
111+
112+
var items = _polyItems;
113+
if (items == null) throw new ObjectDisposedException(nameof(Polynom));
114+
115+
if (_length <= 1)
116+
{
117+
return; // Nothing to sort if the list is empty or contains only one element
118+
}
119+
120+
void QuickSort(int left, int right)
121+
{
122+
int i = left;
123+
int j = right;
124+
PolynomItem pivot = items[(left + right) / 2];
125+
126+
while (i <= j)
127+
{
128+
while (comparer(items[i], pivot) < 0) i++;
129+
while (comparer(items[j], pivot) > 0) j--;
130+
131+
if (i <= j)
132+
{
133+
// Swap items[i] and items[j]
134+
PolynomItem temp = items[i];
135+
items[i] = items[j];
136+
items[j] = temp;
137+
i++;
138+
j--;
139+
}
140+
}
141+
142+
// Recursively sort the sub-arrays
143+
if (left < j) QuickSort(left, j);
144+
if (i < right) QuickSort(i, right);
145+
}
146+
147+
QuickSort(0, _length - 1);
148+
}
26149

27150
/// <summary>
28151
/// Returns a string that represents the polynomial in standard algebraic notation.
@@ -32,7 +155,7 @@ public override string ToString()
32155
{
33156
var sb = new StringBuilder();
34157

35-
foreach (var polyItem in this.PolyItems)
158+
foreach (var polyItem in _polyItems)
36159
{
37160
sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + ");
38161
}
@@ -43,6 +166,137 @@ public override string ToString()
43166

44167
return sb.ToString();
45168
}
169+
170+
/// <inheritdoc/>
171+
public void Dispose()
172+
{
173+
ReturnArray(_polyItems);
174+
_polyItems = null;
175+
}
176+
177+
/// <summary>
178+
/// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms.
179+
/// </summary>
180+
private void AssertCapacity(int min)
181+
{
182+
if (_polyItems.Length < min)
183+
{
184+
// All math by QRCoder should be done with fixed polynomials, so we don't need to grow the capacity.
185+
ThrowNotSupportedException();
186+
187+
// Sample code for growing the capacity:
188+
//var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8
189+
//Array.Copy(_polyItems, newArray, _length);
190+
//ReturnArray(_polyItems);
191+
//_polyItems = newArray;
192+
}
193+
194+
#if NET6_0_OR_GREATER
195+
[StackTraceHidden]
196+
#endif
197+
void ThrowNotSupportedException()
198+
{
199+
throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased.");
200+
}
201+
}
202+
203+
#if NETCOREAPP
204+
/// <summary>
205+
/// Rents memory for the polynomial terms from the shared memory pool.
206+
/// </summary>
207+
private static PolynomItem[] RentArray(int count)
208+
{
209+
return System.Buffers.ArrayPool<PolynomItem>.Shared.Rent(count);
210+
}
211+
212+
/// <summary>
213+
/// Returns memory allocated for the polynomial terms back to the shared memory pool.
214+
/// </summary>
215+
private static void ReturnArray(PolynomItem[] array)
216+
{
217+
System.Buffers.ArrayPool<PolynomItem>.Shared.Return(array);
218+
}
219+
#else
220+
// Implement a poor-man's array pool for .NET Framework
221+
[ThreadStatic]
222+
private static List<PolynomItem[]> _arrayPool;
223+
224+
/// <summary>
225+
/// Rents memory for the polynomial terms from a shared memory pool.
226+
/// </summary>
227+
private static PolynomItem[] RentArray(int count)
228+
{
229+
if (count <= 0)
230+
ThrowArgumentOutOfRangeException();
231+
232+
// Search for a suitable array in the thread-local pool, if it has been initialized
233+
if (_arrayPool != null)
234+
{
235+
for (int i = 0; i < _arrayPool.Count; i++)
236+
{
237+
var array = _arrayPool[i];
238+
if (array.Length >= count)
239+
{
240+
_arrayPool.RemoveAt(i);
241+
return array;
242+
}
243+
}
244+
}
245+
246+
// No suitable buffer found; create a new one
247+
return new PolynomItem[count];
248+
249+
void ThrowArgumentOutOfRangeException()
250+
{
251+
throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number.");
252+
}
253+
}
254+
255+
/// <summary>
256+
/// Returns memory allocated for the polynomial terms back to a shared memory pool.
257+
/// </summary>
258+
private static void ReturnArray(PolynomItem[] array)
259+
{
260+
if (array == null)
261+
ThrowArgumentNullException();
262+
263+
// Initialize the thread-local pool if it's not already done
264+
if (_arrayPool == null)
265+
_arrayPool = new List<PolynomItem[]>(8);
266+
267+
// Add the buffer back to the pool
268+
_arrayPool.Add(array);
269+
270+
void ThrowArgumentNullException()
271+
{
272+
throw new ArgumentNullException(nameof(array));
273+
}
274+
}
275+
#endif
276+
277+
/// <summary>
278+
/// Returns an enumerator that iterates through the polynomial terms.
279+
/// </summary>
280+
public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this);
281+
282+
/// <summary>
283+
/// Value type enumerator for the <see cref="Polynom"/> struct.
284+
/// </summary>
285+
public struct PolynumEnumerator
286+
{
287+
private Polynom _polynom;
288+
private int _index;
289+
290+
public PolynumEnumerator(Polynom polynom)
291+
{
292+
_polynom = polynom;
293+
_index = -1;
294+
}
295+
296+
public PolynomItem Current => _polynom[_index];
297+
298+
public bool MoveNext() => ++_index < _polynom._length;
299+
}
46300
}
47301
}
48302
}

0 commit comments

Comments
 (0)