Skip to content

Commit 6b3e6a0

Browse files
committed
Use thread-local storage for transient memory requirements of blocked cells
1 parent ee872e9 commit 6b3e6a0

File tree

3 files changed

+65
-33
lines changed

3 files changed

+65
-33
lines changed

QRCoder/QRCodeGenerator.ModulePlacer.BlockedModules.cs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections;
1+
using System;
2+
using System.Collections;
23

34
namespace QRCoder
45
{
@@ -9,20 +10,32 @@ private static partial class ModulePlacer
910
/// <summary>
1011
/// Struct that represents blocked modules using rectangles.
1112
/// </summary>
12-
public struct BlockedModules
13+
public struct BlockedModules : IDisposable
1314
{
14-
private readonly BitArray[] _blockedModules;
15+
private readonly bool[,] _blockedModules;
16+
17+
[ThreadStatic]
18+
private static bool[,] _staticBlockedModules;
1519

1620
/// <summary>
1721
/// Initializes a new instance of the <see cref="BlockedModules"/> struct with a specified capacity.
1822
/// </summary>
1923
/// <param name="capacity">The initial capacity of the blocked modules list.</param>
2024
public BlockedModules(int size)
2125
{
22-
_blockedModules = new BitArray[size];
23-
for (int i = 0; i < size; i++)
26+
if (_staticBlockedModules != null && _staticBlockedModules.GetUpperBound(0) >= (size - 1))
27+
{
28+
_blockedModules = _staticBlockedModules;
29+
_staticBlockedModules = null;
30+
#if NET6_0_OR_GREATER
31+
Array.Clear(_blockedModules);
32+
#else
33+
Array.Clear(_blockedModules, 0, _blockedModules.Length);
34+
#endif
35+
}
36+
else
2437
{
25-
_blockedModules[i] = new BitArray(size);
38+
_blockedModules = new bool[size, size];
2639
}
2740
}
2841

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

3952
/// <summary>
@@ -46,7 +59,7 @@ public void Add(Rectangle rect)
4659
{
4760
for (int x = rect.X; x < rect.X + rect.Width; x++)
4861
{
49-
_blockedModules[y][x] = true;
62+
_blockedModules[y, x] = true;
5063
}
5164
}
5265
}
@@ -59,7 +72,7 @@ public void Add(Rectangle rect)
5972
/// <returns><c>true</c> if the coordinates are blocked; otherwise, <c>false</c>.</returns>
6073
public bool IsBlocked(int x, int y)
6174
{
62-
return _blockedModules[y][x];
75+
return _blockedModules[y, x];
6376
}
6477

6578
/// <summary>
@@ -73,12 +86,20 @@ public bool IsBlocked(Rectangle r1)
7386
{
7487
for (int x = r1.X; x < r1.X + r1.Width; x++)
7588
{
76-
if (_blockedModules[y][x])
89+
if (_blockedModules[y, x])
7790
return true;
7891
}
7992
}
8093
return false;
8194
}
95+
96+
public void Dispose()
97+
{
98+
if (_staticBlockedModules == null || _staticBlockedModules.GetUpperBound(0) < _blockedModules.GetUpperBound(0))
99+
{
100+
_staticBlockedModules = _blockedModules;
101+
}
102+
}
82103
}
83104
}
84105
}

QRCoder/QRCodeGenerator.ModulePlacer.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
114114

115115
// Temporary QRCodeData object to test different mask patterns without altering the original.
116116
var qrTemp = new QRCodeData(version, false);
117-
BitArray versionString = version >= 7 ? GetVersionString(version) : null;
117+
BitArray versionString = null;
118+
if (version >= 7)
119+
{
120+
versionString = new BitArray(18);
121+
GetVersionString(versionString, version);
122+
}
123+
var formatStr = new BitArray(15);
118124
foreach (var pattern in MaskPattern.Patterns)
119125
{
120126
// Reset the temporary QR code to the current state of the actual QR code.
@@ -127,7 +133,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
127133
}
128134

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

133139
// Place version information if applicable.
@@ -181,6 +187,7 @@ public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blocke
181187
qrCode.ModuleMatrix[x + 4][x + 4] ^= MaskPattern.Patterns[selectedPattern.Value](x, x);
182188
}
183189
}
190+
184191
return selectedPattern.Value - 1;
185192
}
186193

QRCoder/QRCodeGenerator.cs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -321,22 +321,25 @@ QRCodeData PlaceModules()
321321
{
322322
var qr = new QRCodeData(version, true);
323323
var size = qr.ModuleMatrix.Count - 8;
324-
var blockedModules = new ModulePlacer.BlockedModules(size);
325-
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
326-
ModulePlacer.ReserveSeperatorAreas(size, blockedModules);
327-
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
328-
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
329-
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
330-
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
331-
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
332-
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
333-
var formatStr = GetFormatString(eccLevel, maskVersion);
334-
335-
ModulePlacer.PlaceFormat(qr, formatStr, true);
324+
var tempBitArray = new BitArray(18); //version string requires 18 bits
325+
using (var blockedModules = new ModulePlacer.BlockedModules(size))
326+
{
327+
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
328+
ModulePlacer.ReserveSeperatorAreas(size, blockedModules);
329+
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
330+
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
331+
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
332+
ModulePlacer.ReserveVersionAreas(size, version, blockedModules);
333+
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
334+
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
335+
GetFormatString(tempBitArray, eccLevel, maskVersion);
336+
ModulePlacer.PlaceFormat(qr, tempBitArray, true);
337+
}
338+
336339
if (version >= 7)
337340
{
338-
var versionString = GetVersionString(version);
339-
ModulePlacer.PlaceVersion(qr, versionString, true);
341+
GetVersionString(tempBitArray, version);
342+
ModulePlacer.PlaceVersion(qr, tempBitArray, true);
340343
}
341344

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

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

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

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

455461
var count = vStr.Length;
@@ -471,8 +477,6 @@ private static BitArray GetVersionString(int version)
471477
vStr.Length = 12 + 6;
472478
ShiftAwayFromBit0(vStr, (12 - count) + 6);
473479
DecToBin(version, 6, vStr, 0);
474-
475-
return vStr;
476480
}
477481

478482
/// <summary>

0 commit comments

Comments
 (0)