Skip to content

Commit

Permalink
Use thread-local storage for transient memory requirements of blocked…
Browse files Browse the repository at this point in the history
… cells
  • Loading branch information
Shane32 committed May 19, 2024
1 parent ee872e9 commit 6b3e6a0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 33 deletions.
41 changes: 31 additions & 10 deletions QRCoder/QRCodeGenerator.ModulePlacer.BlockedModules.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System;
using System.Collections;

namespace QRCoder
{
Expand All @@ -9,20 +10,32 @@ private static partial class ModulePlacer
/// <summary>
/// Struct that represents blocked modules using rectangles.
/// </summary>
public struct BlockedModules
public struct BlockedModules : IDisposable
{
private readonly BitArray[] _blockedModules;
private readonly bool[,] _blockedModules;

[ThreadStatic]
private static bool[,] _staticBlockedModules;

/// <summary>
/// Initializes a new instance of the <see cref="BlockedModules"/> struct with a specified capacity.
/// </summary>
/// <param name="capacity">The initial capacity of the blocked modules list.</param>
public BlockedModules(int size)
{
_blockedModules = new BitArray[size];
for (int i = 0; i < size; i++)
if (_staticBlockedModules != null && _staticBlockedModules.GetUpperBound(0) >= (size - 1))
{
_blockedModules = _staticBlockedModules;
_staticBlockedModules = null;
#if NET6_0_OR_GREATER
Array.Clear(_blockedModules);
#else
Array.Clear(_blockedModules, 0, _blockedModules.Length);
#endif
}
else
{
_blockedModules[i] = new BitArray(size);
_blockedModules = new bool[size, size];
}
}

Expand All @@ -33,7 +46,7 @@ public BlockedModules(int size)
/// <param name="y">The y-coordinate of the module.</param>
public void Add(int x, int y)
{
_blockedModules[y][x] = true;
_blockedModules[y, x] = true;
}

/// <summary>
Expand All @@ -46,7 +59,7 @@ public void Add(Rectangle rect)
{
for (int x = rect.X; x < rect.X + rect.Width; x++)
{
_blockedModules[y][x] = true;
_blockedModules[y, x] = true;
}
}
}
Expand All @@ -59,7 +72,7 @@ public void Add(Rectangle rect)
/// <returns><c>true</c> if the coordinates are blocked; otherwise, <c>false</c>.</returns>
public bool IsBlocked(int x, int y)
{
return _blockedModules[y][x];
return _blockedModules[y, x];
}

/// <summary>
Expand All @@ -73,12 +86,20 @@ public bool IsBlocked(Rectangle r1)
{
for (int x = r1.X; x < r1.X + r1.Width; x++)
{
if (_blockedModules[y][x])
if (_blockedModules[y, x])
return true;
}
}
return false;
}

public void Dispose()
{
if (_staticBlockedModules == null || _staticBlockedModules.GetUpperBound(0) < _blockedModules.GetUpperBound(0))
{
_staticBlockedModules = _blockedModules;
}
}
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions QRCoder/QRCodeGenerator.ModulePlacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,13 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke

// Temporary QRCodeData object to test different mask patterns without altering the original.
var qrTemp = new QRCodeData(version, false);
BitArray versionString = version >= 7 ? GetVersionString(version) : null;
BitArray versionString = null;
if (version >= 7)
{
versionString = new BitArray(18);
GetVersionString(versionString, version);
}
var formatStr = new BitArray(15);
foreach (var pattern in MaskPattern.Patterns)
{
// Reset the temporary QR code to the current state of the actual QR code.
Expand All @@ -127,7 +133,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
}

// Place format information using the current mask pattern.
var formatStr = GetFormatString(eccLevel, pattern.Key - 1);
GetFormatString(formatStr, eccLevel, pattern.Key - 1);
ModulePlacer.PlaceFormat(qrTemp, formatStr, false);

// Place version information if applicable.
Expand Down Expand Up @@ -181,6 +187,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
qrCode.ModuleMatrix[x + 4][x + 4] ^= MaskPattern.Patterns[selectedPattern.Value](x, x);
}
}

return selectedPattern.Value - 1;
}

Expand Down
46 changes: 25 additions & 21 deletions QRCoder/QRCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,22 +321,25 @@ QRCodeData PlaceModules()
{
var qr = new QRCodeData(version, true);
var size = qr.ModuleMatrix.Count - 8;
var blockedModules = new ModulePlacer.BlockedModules(size);
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
ModulePlacer.ReserveSeperatorAreas(size, blockedModules);
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
var formatStr = GetFormatString(eccLevel, maskVersion);

ModulePlacer.PlaceFormat(qr, formatStr, true);
var tempBitArray = new BitArray(18); //version string requires 18 bits
using (var blockedModules = new ModulePlacer.BlockedModules(size))
{
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
ModulePlacer.ReserveSeperatorAreas(size, blockedModules);
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
GetFormatString(tempBitArray, eccLevel, maskVersion);
ModulePlacer.PlaceFormat(qr, tempBitArray, true);
}

if (version >= 7)
{
var versionString = GetVersionString(version);
ModulePlacer.PlaceVersion(qr, versionString, true);
GetVersionString(tempBitArray, version);
ModulePlacer.PlaceVersion(qr, tempBitArray, true);
}

return qr;
Expand All @@ -349,12 +352,14 @@ QRCodeData PlaceModules()
/// Generates a BitArray containing the format string for a QR code based on the error correction level and mask pattern version.
/// The format string includes the error correction level, mask pattern version, and error correction coding.
/// </summary>
/// <param name="bitArray">The <see cref="BitArray"/> to write to, or null to create a new one.</param>
/// <param name="level">The error correction level to be encoded in the format string.</param>
/// <param name="maskVersion">The mask pattern version to be encoded in the format string.</param>
/// <returns>A BitArray containing the 15-bit format string used in QR code generation.</returns>
private static BitArray GetFormatString(ECCLevel level, int maskVersion)
private static void GetFormatString(BitArray fStrEcc, ECCLevel level, int maskVersion)
{
var fStrEcc = new BitArray(15); // Total length including space for mask version and padding
fStrEcc.Length = 15;
fStrEcc.SetAll(false);
WriteEccLevelAndVersion();

// Apply the format generator polynomial to add error correction to the format string.
Expand All @@ -378,7 +383,6 @@ private static BitArray GetFormatString(ECCLevel level, int maskVersion)

// XOR the format string with a predefined mask to add robustness against errors.
fStrEcc.Xor(_getFormatMask);
return fStrEcc;

void WriteEccLevelAndVersion()
{
Expand Down Expand Up @@ -445,11 +449,13 @@ private static void ShiftAwayFromBit0(BitArray fStrEcc, int num)
/// Encodes the version information of a QR code into a BitArray using error correction coding similar to format information encoding.
/// This method is used for QR codes version 7 and above.
/// </summary>
/// <param name="vStr">A <see cref="BitArray"/> to write the version string to.</param>
/// <param name="version">The version number of the QR code (7-40).</param>
/// <returns>A BitArray containing the encoded version information, which includes error correction bits.</returns>
private static BitArray GetVersionString(int version)
private static void GetVersionString(BitArray vStr, int version)
{
var vStr = new BitArray(18);
vStr.Length = 18;
vStr.SetAll(false);
DecToBin(version, 6, vStr, 0); // Convert the version number to a 6-bit binary representation.

var count = vStr.Length;
Expand All @@ -471,8 +477,6 @@ private static BitArray GetVersionString(int version)
vStr.Length = 12 + 6;
ShiftAwayFromBit0(vStr, (12 - count) + 6);
DecToBin(version, 6, vStr, 0);

return vStr;
}

/// <summary>
Expand Down

0 comments on commit 6b3e6a0

Please sign in to comment.