Skip to content

Commit

Permalink
Merge pull request codebude#519 from Shane32/calc_generator_polynom_once
Browse files Browse the repository at this point in the history
Utilize ArrayPool for ECC calculations
  • Loading branch information
codebude authored May 12, 2024
2 parents d237ca6 + b0af418 commit 10093b7
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 93 deletions.
266 changes: 260 additions & 6 deletions QRCoder/QRCodeGenerator.Polynom.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace QRCoder
Expand All @@ -8,21 +10,142 @@ public partial class QRCodeGenerator
/// <summary>
/// Represents a polynomial, which is a sum of polynomial terms.
/// </summary>
private struct Polynom
private struct Polynom : IDisposable
{
private PolynomItem[] _polyItems;
private int _length;

/// <summary>
/// Initializes a new instance of the <see cref="Polynom"/> struct with a specified number of initial capacity for polynomial terms.
/// </summary>
/// <param name="count">The initial capacity of the polynomial items list.</param>
public Polynom(int count)
{
this.PolyItems = new List<PolynomItem>(count);
_length = 0;
_polyItems = RentArray(count);
}

/// <summary>
/// Adds a polynomial term to the polynomial.
/// </summary>
public void Add(PolynomItem item)
{
AssertCapacity(_length + 1);
_polyItems[_length++] = item;
}

/// <summary>
/// Removes the polynomial term at the specified index.
/// </summary>
public void RemoveAt(int index)
{
if ((uint)index >= (uint)_length)
throw new IndexOutOfRangeException();

if (index < _length - 1)
Array.Copy(_polyItems, index + 1, _polyItems, index, _length - index - 1);

_length--;
}

/// <summary>
/// Gets or sets a polynomial term at the specified index.
/// </summary>
public PolynomItem this[int index]
{
get {
if ((uint)index >= _length)
ThrowIndexOutOfRangeException();
return _polyItems[index];
}
set {
if ((uint)index >= _length)
ThrowIndexOutOfRangeException();
_polyItems[index] = value;
}
}

#if NET6_0_OR_GREATER
[StackTraceHidden]
#endif
private static void ThrowIndexOutOfRangeException()
{
throw new IndexOutOfRangeException();
}


/// <summary>
/// Gets the number of polynomial terms in the polynomial.
/// </summary>
public int Count => _length;

/// <summary>
/// Removes all polynomial terms from the polynomial.
/// </summary>
public void Clear()
{
_length = 0;
}

/// <summary>
/// Gets or sets the list of polynomial items, where each item represents a term in the polynomial.
/// Clones the polynomial, creating a new instance with the same polynomial terms.
/// </summary>
public List<PolynomItem> PolyItems { get; set; }
public Polynom Clone()
{
var newPolynom = new Polynom(_length);
Array.Copy(_polyItems, newPolynom._polyItems, _length);
newPolynom._length = _length;
return newPolynom;
}

/// <summary>
/// Sorts the collection of <see cref="PolynomItem"/> using a custom comparer function.
/// </summary>
/// <param name="comparer">
/// A function that compares two <see cref="PolynomItem"/> objects and returns an integer indicating their relative order:
/// 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.
/// </param>
public void Sort(Func<PolynomItem, PolynomItem, int> comparer)
{
if (comparer == null) throw new ArgumentNullException(nameof(comparer));

var items = _polyItems;
if (items == null) throw new ObjectDisposedException(nameof(Polynom));

if (_length <= 1)
{
return; // Nothing to sort if the list is empty or contains only one element
}

void QuickSort(int left, int right)
{
int i = left;
int j = right;
PolynomItem pivot = items[(left + right) / 2];

while (i <= j)
{
while (comparer(items[i], pivot) < 0) i++;
while (comparer(items[j], pivot) > 0) j--;

if (i <= j)
{
// Swap items[i] and items[j]
PolynomItem temp = items[i];
items[i] = items[j];
items[j] = temp;
i++;
j--;
}
}

// Recursively sort the sub-arrays
if (left < j) QuickSort(left, j);
if (i < right) QuickSort(i, right);
}

QuickSort(0, _length - 1);
}

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

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

return sb.ToString();
}

/// <inheritdoc/>
public void Dispose()
{
ReturnArray(_polyItems);
_polyItems = null;
}

/// <summary>
/// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms.
/// </summary>
private void AssertCapacity(int min)
{
if (_polyItems.Length < min)
{
// All math by QRCoder should be done with fixed polynomials, so we don't need to grow the capacity.
ThrowNotSupportedException();

// Sample code for growing the capacity:
//var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8
//Array.Copy(_polyItems, newArray, _length);
//ReturnArray(_polyItems);
//_polyItems = newArray;
}

#if NET6_0_OR_GREATER
[StackTraceHidden]
#endif
void ThrowNotSupportedException()
{
throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased.");
}
}

#if NETCOREAPP
/// <summary>
/// Rents memory for the polynomial terms from the shared memory pool.
/// </summary>
private static PolynomItem[] RentArray(int count)
{
return System.Buffers.ArrayPool<PolynomItem>.Shared.Rent(count);
}

/// <summary>
/// Returns memory allocated for the polynomial terms back to the shared memory pool.
/// </summary>
private static void ReturnArray(PolynomItem[] array)
{
System.Buffers.ArrayPool<PolynomItem>.Shared.Return(array);
}
#else
// Implement a poor-man's array pool for .NET Framework
[ThreadStatic]
private static List<PolynomItem[]> _arrayPool;

/// <summary>
/// Rents memory for the polynomial terms from a shared memory pool.
/// </summary>
private static PolynomItem[] RentArray(int count)
{
if (count <= 0)
ThrowArgumentOutOfRangeException();

// Search for a suitable array in the thread-local pool, if it has been initialized
if (_arrayPool != null)
{
for (int i = 0; i < _arrayPool.Count; i++)
{
var array = _arrayPool[i];
if (array.Length >= count)
{
_arrayPool.RemoveAt(i);
return array;
}
}
}

// No suitable buffer found; create a new one
return new PolynomItem[count];

void ThrowArgumentOutOfRangeException()
{
throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number.");
}
}

/// <summary>
/// Returns memory allocated for the polynomial terms back to a shared memory pool.
/// </summary>
private static void ReturnArray(PolynomItem[] array)
{
if (array == null)
ThrowArgumentNullException();

// Initialize the thread-local pool if it's not already done
if (_arrayPool == null)
_arrayPool = new List<PolynomItem[]>(8);

// Add the buffer back to the pool
_arrayPool.Add(array);

void ThrowArgumentNullException()
{
throw new ArgumentNullException(nameof(array));
}
}
#endif

/// <summary>
/// Returns an enumerator that iterates through the polynomial terms.
/// </summary>
public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this);

/// <summary>
/// Value type enumerator for the <see cref="Polynom"/> struct.
/// </summary>
public struct PolynumEnumerator
{
private Polynom _polynom;
private int _index;

public PolynumEnumerator(Polynom polynom)
{
_polynom = polynom;
_index = -1;
}

public PolynomItem Current => _polynom[_index];

public bool MoveNext() => ++_index < _polynom._length;
}
}
}
}
Loading

0 comments on commit 10093b7

Please sign in to comment.