From 9bd5770d37787c6037afaa49b2a3905e84c87131 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 19:59:08 -0400 Subject: [PATCH 1/8] Calculate generator polynom once --- QRCoder/QRCodeGenerator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 9716017d..2b07c3a2 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -222,6 +222,8 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i } } + // Generate the generator polynomial using the number of ECC words. + var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock); //Calculate error correction words var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length); @@ -291,7 +293,7 @@ void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, Bi for (var i = 0; i < blocksInGroup; i++) { var bitBlockList = BinaryStringToBitBlockByteList(bitArray2, offset2, groupLength); - var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo); + var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo, generatorPolynom); codeWordWithECC.Add(new CodewordBlock( bitBlockList, eccWordList) @@ -438,13 +440,11 @@ private static BitArray GetVersionString(int version) /// This method applies polynomial division, using the message polynomial and a generator polynomial, /// to compute the remainder which forms the ECC codewords. /// - private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) + private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynom) { var eccWords = eccInfo.ECCPerBlock; // Calculate the message polynomial from the bit array data. var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); - // Generate the generator polynomial using the number of ECC words. - var generatorPolynom = CalculateGeneratorPolynom(eccWords); // Adjust the exponents in the message polynomial to account for ECC length. for (var i = 0; i < messagePolynom.PolyItems.Count; i++) From 5af6b6bb45890203a86c57828f97e3c9c531ac79 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 22:47:38 -0400 Subject: [PATCH 2/8] Update Polynom --- QRCoder/QRCodeGenerator.Polynom.cs | 253 ++++++++++++++++++++++++++++- QRCoder/QRCodeGenerator.cs | 159 ++++++++++-------- 2 files changed, 338 insertions(+), 74 deletions(-) diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index 47244ba8..8e6d3c86 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text; +using System.Threading; namespace QRCoder { @@ -8,21 +10,132 @@ public partial class QRCodeGenerator /// /// Represents a polynomial, which is a sum of polynomial terms. /// - private struct Polynom + private struct Polynom : IDisposable { + private PolynomItem[] _polyItems; + private int _length; + /// /// Initializes a new instance of the struct with a specified number of initial capacity for polynomial terms. /// /// The initial capacity of the polynomial items list. public Polynom(int count) { - this.PolyItems = new List(count); + _length = 0; + _polyItems = Allocate(count); + } + + /// + /// Adds a polynomial term to the polynomial. + /// + public void Add(PolynomItem item) + { + EnsureCapacity(_length + 1); + _polyItems[_length++] = item; + } + + /// + /// Removes the polynomial term at the specified index. + /// + public void RemoveAt(int index) + { + if (index < 0 || index >= _length) + throw new IndexOutOfRangeException(); + + if (index < _length - 1) + Array.Copy(_polyItems, index + 1, _polyItems, index, _length - index - 1); + + _length--; + } + + /// + /// Gets or sets a polynomial term at the specified index. + /// + public PolynomItem this[int index] + { + get { + if (index < 0 || index >= _length) + throw new IndexOutOfRangeException(); + return _polyItems[index]; + } + set { + if (index < 0 || index >= _length) + throw new IndexOutOfRangeException(); + _polyItems[index] = value; + } + } + + /// + /// Gets the number of polynomial terms in the polynomial. + /// + public int Count => _length; + + /// + /// Removes all polynomial terms from the polynomial. + /// + public void Clear() + { + _length = 0; + } + + /// + /// Clones the polynomial, creating a new instance with the same polynomial terms. + /// + public Polynom Clone() + { + var newPolynom = new Polynom(_length); + Array.Copy(_polyItems, newPolynom._polyItems, _length); + newPolynom._length = _length; + return newPolynom; } /// - /// Gets or sets the list of polynomial items, where each item represents a term in the polynomial. + /// Sorts the collection of using a custom comparer function. /// - public List PolyItems { get; set; } + /// + /// A function that compares two 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. + /// + public void Sort(Func comparer) + { + if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + + // Assuming you have a list or array to sort within your class + var items = _polyItems; + if (items == null || _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); + } /// /// Returns a string that represents the polynomial in standard algebraic notation. @@ -32,7 +145,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 + " + "); } @@ -43,6 +156,134 @@ public override string ToString() return sb.ToString(); } + + /// + public void Dispose() + { + Free(_polyItems); + _polyItems = null; + } + + /// + /// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms. + /// + private void EnsureCapacity(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 = Allocate(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 + //Array.Copy(_polyItems, newArray, _length); + //Free(_polyItems); + //_polyItems = newArray; + } + + void ThrowNotSupportedException() + { + throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased."); + } + } + +#if NETCOREAPP + /// + /// Allocates memory for the polynomial terms. + /// + private static PolynomItem[] Allocate(int count) + { + return System.Buffers.ArrayPool.Shared.Rent(count); + } + + /// + /// Frees memory allocated for the polynomial terms. + /// + private static void Free(PolynomItem[] array) + { + System.Buffers.ArrayPool.Shared.Return(array); + } +#else + // Implement a poor-man's array pool for .NET Framework + [ThreadStatic] + private static List _arrayPool; + + /// + /// Allocates memory for the polynomial terms. + /// + private static PolynomItem[] Allocate(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."); + } + } + + /// + /// Frees memory allocated for the polynomial terms. + /// + private static void Free(PolynomItem[] array) + { + if (array == null) + ThrowArgumentNullException(); + + // Initialize the thread-local pool if it's not already done + if (_arrayPool == null) + _arrayPool = new List(8); + + // Add the buffer back to the pool + _arrayPool.Add(array); + + void ThrowArgumentNullException() + { + throw new ArgumentNullException(nameof(array)); + } + } +#endif + + /// + /// Returns an enumerator that iterates through the polynomial terms. + /// + public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this); + + /// + /// Value type enumerator for the struct. + /// + 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; + } } } } diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 2b07c3a2..9bb9f17d 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -223,13 +223,15 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i } // Generate the generator polynomial using the number of ECC words. - var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock); - //Calculate error correction words - var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length); - int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; - AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset); - + List codeWordWithECC; + using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) + { + //Calculate error correction words + codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); + AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length, generatorPolynom); + int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; + AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset, generatorPolynom); + } //Calculate interleaved code word lengths int interleavedLength = 0; @@ -287,7 +289,7 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i ModulePlacer.AddQuietZone(qr); return qr; - void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count) + void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count, Polynom generatorPolynom) { var groupLength = codewordsInGroup * 8; for (var i = 0; i < blocksInGroup; i++) @@ -440,44 +442,61 @@ private static BitArray GetVersionString(int version) /// This method applies polynomial division, using the message polynomial and a generator polynomial, /// to compute the remainder which forms the ECC codewords. /// - private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynom) + private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynomBase) { var eccWords = eccInfo.ECCPerBlock; // Calculate the message polynomial from the bit array data. var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); + var generatorPolynom = generatorPolynomBase.Clone(); // Adjust the exponents in the message polynomial to account for ECC length. - for (var i = 0; i < messagePolynom.PolyItems.Count; i++) - messagePolynom.PolyItems[i] = new PolynomItem(messagePolynom.PolyItems[i].Coefficient, - messagePolynom.PolyItems[i].Exponent + eccWords); + for (var i = 0; i < messagePolynom.Count; i++) + messagePolynom[i] = new PolynomItem(messagePolynom[i].Coefficient, + messagePolynom[i].Exponent + eccWords); // Adjust the generator polynomial exponents based on the message polynomial. - for (var i = 0; i < generatorPolynom.PolyItems.Count; i++) - generatorPolynom.PolyItems[i] = new PolynomItem(generatorPolynom.PolyItems[i].Coefficient, - generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count - 1)); + for (var i = 0; i < generatorPolynom.Count; i++) + generatorPolynom[i] = new PolynomItem(generatorPolynom[i].Coefficient, + generatorPolynom[i].Exponent + (messagePolynom.Count - 1)); // Divide the message polynomial by the generator polynomial to find the remainder. var leadTermSource = messagePolynom; - for (var i = 0; (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0); i++) + for (var i = 0; (leadTermSource.Count > 0 && leadTermSource[leadTermSource.Count - 1].Exponent > 0); i++) { - if (leadTermSource.PolyItems[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. + if (leadTermSource[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. { - leadTermSource.PolyItems.RemoveAt(0); - leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); + leadTermSource.RemoveAt(0); + leadTermSource.Add(new PolynomItem(0, leadTermSource[leadTermSource.Count - 1].Exponent - 1)); } else // Otherwise, perform polynomial reduction using XOR and multiplication with the generator polynomial. { - var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); + // Convert the first coefficient to its corresponding alpha exponent unless it's zero. + // Coefficients that are zero remain zero because log(0) is undefined. + var index0Coefficient = leadTermSource[0].Coefficient; + index0Coefficient = index0Coefficient == 0 ? 0 : GetAlphaExpFromIntVal(index0Coefficient); + var alphaNotation = new PolynomItem(index0Coefficient, leadTermSource[0].Exponent); + var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, alphaNotation, i); ConvertToDecNotationInPlace(resPoly); - resPoly = XORPolynoms(leadTermSource, resPoly); - leadTermSource = resPoly; + var newPoly = XORPolynoms(leadTermSource, resPoly); + // Free memory used by the previous polynomials. + resPoly.Dispose(); + leadTermSource.Dispose(); + // Update the message polynomial with the new remainder. + leadTermSource = newPoly; } } + // Free memory used by the generator polynomial. + generatorPolynom.Dispose(); + // Convert the resulting polynomial into a byte array representing the ECC codewords. - var ret = new byte[leadTermSource.PolyItems.Count]; - for (var i = 0; i < leadTermSource.PolyItems.Count; i++) - ret[i] = (byte)leadTermSource.PolyItems[i].Coefficient; + var ret = new byte[leadTermSource.Count]; + for (var i = 0; i < leadTermSource.Count; i++) + ret[i] = (byte)leadTermSource[i].Coefficient; + + // Free memory used by the message polynomial. + leadTermSource.Dispose(); + return ret; } @@ -488,18 +507,18 @@ private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count /// private static Polynom ConvertToAlphaNotation(Polynom poly) { - var newPoly = new Polynom(poly.PolyItems.Count); + var newPoly = new Polynom(poly.Count); - for (var i = 0; i < poly.PolyItems.Count; i++) + for (var i = 0; i < poly.Count; i++) { // Convert each coefficient to its corresponding alpha exponent unless it's zero. // Coefficients that are zero remain zero because log(0) is undefined. - newPoly.PolyItems.Add( + newPoly.Add( new PolynomItem( - (poly.PolyItems[i].Coefficient != 0 - ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) + (poly[i].Coefficient != 0 + ? GetAlphaExpFromIntVal(poly[i].Coefficient) : 0), - poly.PolyItems[i].Exponent)); // The exponent remains unchanged. + poly[i].Exponent)); // The exponent remains unchanged. } return newPoly; @@ -511,10 +530,10 @@ private static Polynom ConvertToAlphaNotation(Polynom poly) /// private static void ConvertToDecNotationInPlace(Polynom poly) { - for (var i = 0; i < poly.PolyItems.Count; i++) + for (var i = 0; i < poly.Count; i++) { // Convert the alpha exponent of the coefficient to its decimal value and create a new polynomial item with the updated coefficient. - poly.PolyItems[i] = new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent); + poly[i] = new PolynomItem(GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); } } @@ -587,7 +606,7 @@ private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, in var messagePol = new Polynom(count /= 8); for (var i = count - 1; i >= 0; i--) { - messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); + messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); offset += 8; } return messagePol; @@ -601,19 +620,23 @@ private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, in private static Polynom CalculateGeneratorPolynom(int numEccWords) { var generatorPolynom = new Polynom(2); // Start with the simplest form of the polynomial - generatorPolynom.PolyItems.Add(new PolynomItem(0, 1)); - generatorPolynom.PolyItems.Add(new PolynomItem(0, 0)); + generatorPolynom.Add(new PolynomItem(0, 1)); + generatorPolynom.Add(new PolynomItem(0, 0)); - var multiplierPolynom = new Polynom(numEccWords * 2); // Used for polynomial multiplication - for (var i = 1; i <= numEccWords - 1; i++) + using (var multiplierPolynom = new Polynom(numEccWords * 2)) // Used for polynomial multiplication { - // Clear and set up the multiplier polynomial for the current multiplication - multiplierPolynom.PolyItems.Clear(); - multiplierPolynom.PolyItems.Add(new PolynomItem(0, 1)); - multiplierPolynom.PolyItems.Add(new PolynomItem(i, 0)); - - // Multiply the generator polynomial by the current multiplier polynomial - generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + for (var i = 1; i <= numEccWords - 1; i++) + { + // Clear and set up the multiplier polynomial for the current multiplication + multiplierPolynom.Clear(); + multiplierPolynom.Add(new PolynomItem(0, 1)); + multiplierPolynom.Add(new PolynomItem(i, 0)); + + // Multiply the generator polynomial by the current multiplier polynomial + var newGeneratorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + generatorPolynom.Dispose(); + generatorPolynom = newGeneratorPolynom; + } } return generatorPolynom; // Return the completed generator polynomial @@ -956,9 +979,9 @@ private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) { // Determine the larger of the two polynomials to guide the XOR operation. - var resultPolynom = new Polynom(Math.Max(messagePolynom.PolyItems.Count, resPolynom.PolyItems.Count)); + var resultPolynom = new Polynom(Math.Max(messagePolynom.Count, resPolynom.Count) - 1); Polynom longPoly, shortPoly; - if (messagePolynom.PolyItems.Count >= resPolynom.PolyItems.Count) + if (messagePolynom.Count >= resPolynom.Count) { longPoly = messagePolynom; shortPoly = resPolynom; @@ -970,16 +993,16 @@ private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) } // XOR the coefficients of the two polynomials. - for (var i = 0; i < longPoly.PolyItems.Count; i++) + for (var i = 1; i < longPoly.Count; i++) { var polItemRes = new PolynomItem( - longPoly.PolyItems[i].Coefficient ^ - (shortPoly.PolyItems.Count > i ? shortPoly.PolyItems[i].Coefficient : 0), - messagePolynom.PolyItems[0].Exponent - i + longPoly[i].Coefficient ^ + (shortPoly.Count > i ? shortPoly[i].Coefficient : 0), + messagePolynom[0].Exponent - i ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } - resultPolynom.PolyItems.RemoveAt(0); + return resultPolynom; } @@ -989,15 +1012,15 @@ private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) /// private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) { - var resultPolynom = new Polynom(genPolynom.PolyItems.Count); - foreach (var polItemBase in genPolynom.PolyItems) + var resultPolynom = new Polynom(genPolynom.Count); + foreach (var polItemBase in genPolynom) { var polItemRes = new PolynomItem( (polItemBase.Coefficient + leadTerm.Coefficient) % 255, polItemBase.Exponent - lowerExponentBy ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } return resultPolynom; } @@ -1011,12 +1034,12 @@ private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, Po private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) { // Initialize a new polynomial with a size based on the product of the sizes of the two input polynomials. - var resultPolynom = new Polynom(polynomMultiplier.PolyItems.Count * polynomBase.PolyItems.Count); + var resultPolynom = new Polynom(polynomMultiplier.Count * polynomBase.Count); // Multiply each term of the first polynomial by each term of the second polynomial. - foreach (var polItemBase in polynomMultiplier.PolyItems) + foreach (var polItemBase in polynomMultiplier) { - foreach (var polItemMulti in polynomBase.PolyItems) + foreach (var polItemMulti in polynomBase) { // Create a new polynomial term with the coefficients added (as exponents) and exponents summed. var polItemRes = new PolynomItem @@ -1024,18 +1047,18 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), (polItemBase.Exponent + polItemMulti.Exponent) ); - resultPolynom.PolyItems.Add(polItemRes); + resultPolynom.Add(polItemRes); } } // Identify and merge terms with the same exponent. - var toGlue = GetNotUniqueExponents(resultPolynom.PolyItems); + var toGlue = GetNotUniqueExponents(resultPolynom); var gluedPolynoms = new PolynomItem[toGlue.Length]; var gluedPolynomsIndex = 0; foreach (var exponent in toGlue) { var coefficient = 0; - foreach (var polynomOld in resultPolynom.PolyItems) + foreach (var polynomOld in resultPolynom) { if (polynomOld.Exponent == exponent) coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); @@ -1047,18 +1070,18 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno } // Remove duplicated exponents and add the corrected ones back. - for (int i = resultPolynom.PolyItems.Count - 1; i >= 0; i--) - if (toGlue.Contains(resultPolynom.PolyItems[i].Exponent)) - resultPolynom.PolyItems.RemoveAt(i); + for (int i = resultPolynom.Count - 1; i >= 0; i--) + if (toGlue.Contains(resultPolynom[i].Exponent)) + resultPolynom.RemoveAt(i); foreach (var polynom in gluedPolynoms) - resultPolynom.PolyItems.Add(polynom); + resultPolynom.Add(polynom); // Sort the polynomial terms by exponent in descending order. - resultPolynom.PolyItems.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); + resultPolynom.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); return resultPolynom; // Auxiliary function to identify exponents that appear more than once in the polynomial. - int[] GetNotUniqueExponents(List list) + int[] GetNotUniqueExponents(Polynom list) { var dic = new Dictionary(list.Count); foreach (var row in list) From d8edc00b984372649629ad5e5eaed1122e48f8df Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 22:52:50 -0400 Subject: [PATCH 3/8] Remove excess using --- QRCoder/QRCodeGenerator.Polynom.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index 8e6d3c86..a5cd04e4 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using System.Threading; namespace QRCoder { From a6bb4dee3f1a9fb2df6f26ba8d9c2a67b83c65be Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 22:54:38 -0400 Subject: [PATCH 4/8] Delete unused method --- QRCoder/QRCodeGenerator.cs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 9bb9f17d..36bc74d0 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -500,30 +500,6 @@ private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count return ret; } - /// - /// Converts the coefficients of a polynomial from integer values to their corresponding alpha exponent notation - /// based on a Galois field mapping. This is typically used in error correction calculations where - /// operations are performed on exponents rather than coefficients directly. - /// - private static Polynom ConvertToAlphaNotation(Polynom poly) - { - var newPoly = new Polynom(poly.Count); - - for (var i = 0; i < poly.Count; i++) - { - // Convert each coefficient to its corresponding alpha exponent unless it's zero. - // Coefficients that are zero remain zero because log(0) is undefined. - newPoly.Add( - new PolynomItem( - (poly[i].Coefficient != 0 - ? GetAlphaExpFromIntVal(poly[i].Coefficient) - : 0), - poly[i].Exponent)); // The exponent remains unchanged. - } - - return newPoly; - } - /// /// Converts all polynomial item coefficients from their alpha exponent notation to decimal representation in place. /// This conversion facilitates operations that require polynomial coefficients in their integer forms. From 9891adb8a0f1384e8a5fadba788dd6e971183435 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 22:56:29 -0400 Subject: [PATCH 5/8] Update comment in sort algorithm --- QRCoder/QRCodeGenerator.Polynom.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index a5cd04e4..e6ce8327 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -99,7 +99,6 @@ public void Sort(Func comparer) { if (comparer == null) throw new ArgumentNullException(nameof(comparer)); - // Assuming you have a list or array to sort within your class var items = _polyItems; if (items == null || _length <= 1) { From a83f55cd93c5029663d322d2836c5d14b6670b92 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Mon, 6 May 2024 23:51:01 -0400 Subject: [PATCH 6/8] Add CreateQRCodeLongest benchmark --- QRCoderBenchmarks/QRCodeGenerator.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/QRCoderBenchmarks/QRCodeGenerator.cs b/QRCoderBenchmarks/QRCodeGenerator.cs index f05ba6d4..742d5be5 100644 --- a/QRCoderBenchmarks/QRCodeGenerator.cs +++ b/QRCoderBenchmarks/QRCodeGenerator.cs @@ -20,4 +20,12 @@ public void CreateQRCodeLong() QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); _ = qrGenerator.CreateQrCode(payload, QRCoder.QRCodeGenerator.ECCLevel.H); } + + [Benchmark] + public void CreateQRCodeLongest() + { + var str = new string('a', 2600); + QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); + _ = qrGenerator.CreateQrCode(str, QRCoder.QRCodeGenerator.ECCLevel.L); + } } From 6101a18fc4e15dc7ba3508ee9d6699f4a75f1381 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 9 May 2024 10:17:02 -0400 Subject: [PATCH 7/8] Update QRCoder/QRCodeGenerator.Polynom.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.Polynom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index e6ce8327..de4afc54 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -38,7 +38,7 @@ public void Add(PolynomItem item) /// public void RemoveAt(int index) { - if (index < 0 || index >= _length) + if ((uint)index >= (uint)_length) throw new IndexOutOfRangeException(); if (index < _length - 1) From b0af41889c52a7ff9cad34a1a0d84987e546c8b3 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 11 May 2024 11:20:59 -0400 Subject: [PATCH 8/8] Apply code suggestions --- QRCoder/QRCodeGenerator.Polynom.cs | 57 +++++++++++++++++++----------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/QRCoder/QRCodeGenerator.Polynom.cs b/QRCoder/QRCodeGenerator.Polynom.cs index de4afc54..90d7239a 100644 --- a/QRCoder/QRCodeGenerator.Polynom.cs +++ b/QRCoder/QRCodeGenerator.Polynom.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; namespace QRCoder @@ -21,7 +22,7 @@ private struct Polynom : IDisposable public Polynom(int count) { _length = 0; - _polyItems = Allocate(count); + _polyItems = RentArray(count); } /// @@ -29,7 +30,7 @@ public Polynom(int count) /// public void Add(PolynomItem item) { - EnsureCapacity(_length + 1); + AssertCapacity(_length + 1); _polyItems[_length++] = item; } @@ -53,17 +54,26 @@ public void RemoveAt(int index) public PolynomItem this[int index] { get { - if (index < 0 || index >= _length) - throw new IndexOutOfRangeException(); + if ((uint)index >= _length) + ThrowIndexOutOfRangeException(); return _polyItems[index]; } set { - if (index < 0 || index >= _length) - throw new IndexOutOfRangeException(); + if ((uint)index >= _length) + ThrowIndexOutOfRangeException(); _polyItems[index] = value; } } +#if NET6_0_OR_GREATER + [StackTraceHidden] +#endif + private static void ThrowIndexOutOfRangeException() + { + throw new IndexOutOfRangeException(); + } + + /// /// Gets the number of polynomial terms in the polynomial. /// @@ -100,7 +110,9 @@ public void Sort(Func comparer) if (comparer == null) throw new ArgumentNullException(nameof(comparer)); var items = _polyItems; - if (items == null || _length <= 1) + 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 } @@ -158,14 +170,14 @@ public override string ToString() /// public void Dispose() { - Free(_polyItems); + ReturnArray(_polyItems); _polyItems = null; } /// /// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms. /// - private void EnsureCapacity(int min) + private void AssertCapacity(int min) { if (_polyItems.Length < min) { @@ -173,12 +185,15 @@ private void EnsureCapacity(int min) ThrowNotSupportedException(); // Sample code for growing the capacity: - //var newArray = Allocate(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 + //var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 //Array.Copy(_polyItems, newArray, _length); - //Free(_polyItems); + //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."); @@ -187,17 +202,17 @@ void ThrowNotSupportedException() #if NETCOREAPP /// - /// Allocates memory for the polynomial terms. + /// Rents memory for the polynomial terms from the shared memory pool. /// - private static PolynomItem[] Allocate(int count) + private static PolynomItem[] RentArray(int count) { return System.Buffers.ArrayPool.Shared.Rent(count); } /// - /// Frees memory allocated for the polynomial terms. + /// Returns memory allocated for the polynomial terms back to the shared memory pool. /// - private static void Free(PolynomItem[] array) + private static void ReturnArray(PolynomItem[] array) { System.Buffers.ArrayPool.Shared.Return(array); } @@ -205,11 +220,11 @@ private static void Free(PolynomItem[] array) // Implement a poor-man's array pool for .NET Framework [ThreadStatic] private static List _arrayPool; - + /// - /// Allocates memory for the polynomial terms. + /// Rents memory for the polynomial terms from a shared memory pool. /// - private static PolynomItem[] Allocate(int count) + private static PolynomItem[] RentArray(int count) { if (count <= 0) ThrowArgumentOutOfRangeException(); @@ -236,11 +251,11 @@ void ThrowArgumentOutOfRangeException() throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number."); } } - + /// - /// Frees memory allocated for the polynomial terms. + /// Returns memory allocated for the polynomial terms back to a shared memory pool. /// - private static void Free(PolynomItem[] array) + private static void ReturnArray(PolynomItem[] array) { if (array == null) ThrowArgumentNullException();