Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deduplicate data block buffers #520

Merged
merged 8 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions QRCoder/Extensions/BitArrayExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections;

namespace QRCoder
{
/// <summary>
/// Helper methods for <see cref="BitArray"/>.
/// </summary>
internal static class BitArrayExtensions
{
/// <summary>
/// Copies a specified number of elements from one <see cref="BitArray"/> to another starting at the specified offsets.
Shane32 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="source">The source <see cref="BitArray"/> from which elements will be copied.</param>
/// <param name="sourceOffset">The zero-based index in the source <see cref="BitArray"/> at which copying begins.</param>
/// <param name="destination">The destination <see cref="BitArray"/> to which elements will be copied.</param>
/// <param name="destinationOffset">The zero-based index in the destination <see cref="BitArray"/> at which storing begins.</param>
/// <param name="count">The number of elements to copy.</param>
/// <returns>The index in the destination <see cref="BitArray"/> immediately following the last copied element.</returns>
public static int CopyTo(this BitArray source, BitArray destination, int sourceOffset, int destinationOffset, int count)
{
for (int i = 0; i < count; i++)
{
destination[destinationOffset + i] = source[sourceOffset + i];
}
return destinationOffset + count;
}
}
}
21 changes: 15 additions & 6 deletions QRCoder/QRCodeGenerator.CodewordBlock.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace QRCoder
using System.Collections;

namespace QRCoder
{
public partial class QRCodeGenerator
{
Expand All @@ -11,18 +13,25 @@ private struct CodewordBlock
/// <summary>
/// Initializes a new instance of the CodewordBlock struct with specified arrays of code words and error correction (ECC) words.
/// </summary>
/// <param name="codeWords">The array of data codewords for this block. Data codewords carry the actual information.</param>
/// <param name="codeWordsOffset">The offset of the data codewords within the main BitArray. Data codewords carry the actual information.</param>
/// <param name="codeWordsLength">The length in bits of the data codewords within the main BitArray.</param>
/// <param name="eccWords">The array of error correction codewords for this block. These codewords help recover the data if the QR code is damaged.</param>
public CodewordBlock(byte[] codeWords, byte[] eccWords)
public CodewordBlock(int codeWordsOffset, int codeWordsLength, byte[] eccWords)
{
this.CodeWords = codeWords;
this.CodeWordsOffset = codeWordsOffset;
this.CodeWordsLength = codeWordsLength;
this.ECCWords = eccWords;
}

/// <summary>
/// Gets the data codewords associated with this block.
/// Gets the offset of the data codewords in the BitArray.
/// </summary>
public int CodeWordsOffset { get; }

/// <summary>
/// Gets the length of the data codewords in the BitArray.
/// </summary>
public byte[] CodeWords { get; }
public int CodeWordsLength { get; }

/// <summary>
/// Gets the error correction codewords associated with this block.
Expand Down
53 changes: 10 additions & 43 deletions QRCoder/QRCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,17 +228,17 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i
{
//Calculate error correction words
codeWordWithECC = new List<CodewordBlock>(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2);
AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length, generatorPolynom);
AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom);
int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8;
AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset, generatorPolynom);
AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom);
}

//Calculate interleaved code word lengths
int interleavedLength = 0;
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
{
foreach (var codeBlock in codeWordWithECC)
if (codeBlock.CodeWords.Length > i)
if ((uint)codeBlock.CodeWordsLength / 8 > i)
interleavedLength += 8;
}
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
Expand All @@ -255,8 +255,10 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
{
foreach (var codeBlock in codeWordWithECC)
if (codeBlock.CodeWords.Length > i)
pos = DecToBin(codeBlock.CodeWords[i], 8, interleavedData, pos);
{
if ((uint)codeBlock.CodeWordsLength / 8 > i)
pos = bitArray.CopyTo(interleavedData, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8);
}
}
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
{
Expand Down Expand Up @@ -289,17 +291,13 @@ 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, Polynom generatorPolynom)
void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom)
{
var groupLength = codewordsInGroup * 8;
for (var i = 0; i < blocksInGroup; i++)
{
var bitBlockList = BinaryStringToBitBlockByteList(bitArray2, offset2, groupLength);
var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo, generatorPolynom);
codeWordWithECC.Add(new CodewordBlock(
bitBlockList,
eccWordList)
);
var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom);
codeWordWithECC.Add(new CodewordBlock(offset2, groupLength, eccWordList));
offset2 += groupLength;
}
}
Expand Down Expand Up @@ -618,37 +616,6 @@ private static Polynom CalculateGeneratorPolynom(int numEccWords)
return generatorPolynom; // Return the completed generator polynomial
}

/// <summary>
/// Converts a segment of a BitArray into a list of bytes where each byte represents a consecutive block of 8 bits from the BitArray.
/// </summary>
private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int offset, int count)
{
const int blockSize = 8;
if (count % blockSize != 0)
ThrowCountMustBeMultipleOf8Exception();
var numberOfBlocks = (int)((uint)count / blockSize);
var blocklist = new byte[numberOfBlocks];

int j = 0;
count += offset;
for (int i = offset; i < count; i += blockSize)
{
blocklist[j++] = (byte)(
(bitString[i] ? 128 : 0) +
(bitString[i + 1] ? 64 : 0) +
(bitString[i + 2] ? 32 : 0) +
(bitString[i + 3] ? 16 : 0) +
(bitString[i + 4] ? 8 : 0) +
(bitString[i + 5] ? 4 : 0) +
(bitString[i + 6] ? 2 : 0) +
(bitString[i + 7] ? 1 : 0));
}

return blocklist;

void ThrowCountMustBeMultipleOf8Exception() => throw new ArgumentOutOfRangeException(nameof(count), "Count must be a multiple of 8.");
}

/// <summary>
/// Converts a segment of a BitArray into its decimal (integer) equivalent.
/// </summary>
Expand Down