Skip to content

Commit ec52e48

Browse files
PngByteQRCode: use ArrayPool.Rent to prevent byte[] allocation (#615)
* feat: use `ArrayPool.Rent` to prevent `byte[]` allocation * chore: fix whitespace --------- Co-authored-by: Shane Krueger <[email protected]>
1 parent a03e28f commit ec52e48

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

QRCoder/PngByteQRCode.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1
2+
using System.Buffers;
3+
#endif
14
using System;
25
using System.IO;
36
using System.IO.Compression;
@@ -36,7 +39,11 @@ public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true)
3639
using var png = new PngBuilder();
3740
var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
3841
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale);
39-
png.WriteScanlines(DrawScanlines(pixelsPerModule, drawQuietZones));
42+
var scanLines = DrawScanlines(pixelsPerModule, drawQuietZones);
43+
png.WriteScanlines(scanLines);
44+
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1
45+
ArrayPool<byte>.Shared.Return(scanLines.Array!);
46+
#endif
4047
png.WriteEnd();
4148
return png.GetBytes();
4249
}
@@ -68,7 +75,11 @@ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] light
6875
var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
6976
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed);
7077
png.WritePalette(darkColorRgba, lightColorRgba);
71-
png.WriteScanlines(DrawScanlines(pixelsPerModule, drawQuietZones));
78+
var scanLines = DrawScanlines(pixelsPerModule, drawQuietZones);
79+
png.WriteScanlines(scanLines);
80+
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1
81+
ArrayPool<byte>.Shared.Return(scanLines.Array!);
82+
#endif
7283
png.WriteEnd();
7384
return png.GetBytes();
7485
}
@@ -79,13 +90,19 @@ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] light
7990
/// <param name="pixelsPerModule">The number of pixels each dark/light module of the QR code will occupy in the final QR code image.</param>
8091
/// <param name="drawQuietZones">Indicates if quiet zones around the QR code should be drawn.</param>
8192
/// <returns>Returns the bitmap as a byte array.</returns>
82-
private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones)
93+
private ArraySegment<byte> DrawScanlines(int pixelsPerModule, bool drawQuietZones)
8394
{
8495
var moduleMatrix = QrCodeData.ModuleMatrix;
8596
var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8);
8697
var quietZoneOffset = (drawQuietZones ? 0 : 4);
8798
var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel.
88-
var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule];
99+
var scanLinesLength = bytesPerScanline * matrixSize * pixelsPerModule;
100+
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1
101+
var scanlines = ArrayPool<byte>.Shared.Rent(scanLinesLength);
102+
Array.Clear(scanlines, 0, scanLinesLength);
103+
#else
104+
var scanlines = new byte[scanLinesLength];
105+
#endif
89106

90107
for (var y = 0; y < matrixSize; y++)
91108
{
@@ -115,7 +132,7 @@ private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones)
115132
}
116133
}
117134

118-
return scanlines;
135+
return new ArraySegment<byte>(scanlines, 0, scanLinesLength);
119136
}
120137

121138
/// <summary>
@@ -249,7 +266,7 @@ public void WritePalette(params byte[][] rgbaColors)
249266
/// <summary>
250267
/// Writes the IDAT chunk with the actual picture.
251268
/// </summary>
252-
public void WriteScanlines(byte[] scanlines)
269+
public void WriteScanlines(ArraySegment<byte> scanlines)
253270
{
254271
using var idatStream = new MemoryStream();
255272
Deflate(idatStream, scanlines);
@@ -268,7 +285,7 @@ public void WriteScanlines(byte[] scanlines)
268285
idatStream.CopyTo(_stream);
269286
#endif
270287
// Deflate checksum.
271-
var adler = Adler32(scanlines, 0, scanlines.Length);
288+
var adler = Adler32(scanlines.Array!, 0, scanlines.Count);
272289
WriteIntBigEndian(adler);
273290

274291
WriteChunkEnd();
@@ -304,10 +321,10 @@ private void WriteIntBigEndian(uint value)
304321
_stream.WriteByte((byte)value);
305322
}
306323

307-
private static void Deflate(Stream output, byte[] bytes)
324+
private static void Deflate(Stream output, ArraySegment<byte> bytes)
308325
{
309326
using var deflateStream = new DeflateStream(output, CompressionMode.Compress, leaveOpen: true);
310-
deflateStream.Write(bytes, 0, bytes.Length);
327+
deflateStream.Write(bytes.Array!, 0, bytes.Count);
311328
}
312329

313330
// Reference implementation from RFC 1950. Not optimized.

0 commit comments

Comments
 (0)