Skip to content

Commit c821821

Browse files
committed
.
1 parent d58ace8 commit c821821

3 files changed

Lines changed: 75 additions & 67 deletions

File tree

src/DeterministicIoPackaging/DeterministicIoPackaging.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
<ItemGroup>
66
<PackageReference Include="Polyfill" PrivateAssets="all" />
77
<PackageReference Include="ProjectDefaults" PrivateAssets="all" />
8-
<PackageReference Include="System.IO.Hashing" />
98
<PackageReference Include="Microsoft.Sbom.Targets" PrivateAssets="all" Condition="'$(CI)' == 'true'" />
109
</ItemGroup>
1110
<ItemGroup Condition="'$(TargetFramework)' == 'net472' OR '$(TargetFramework)' == 'net48'">
11+
<PackageReference Include="System.Threading.Tasks.Extensions" />
1212
<PackageReference Include="System.Buffers" />
13+
<PackageReference Include="System.IO.Hashing" />
1314
<PackageReference Include="System.IO.Compression" />
1415
<PackageReference Include="System.Memory" />
1516
</ItemGroup>
16-
</Project>
17+
</Project>

src/DeterministicIoPackaging/PngNormalizer.cs

Lines changed: 71 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,97 +10,103 @@ static class PngNormalizer
1010

1111
public static void Normalize(Stream source, Stream target)
1212
{
13-
using var buffer = new MemoryStream();
14-
source.CopyTo(buffer);
15-
Normalize(buffer.GetBuffer(), (int) buffer.Length, target);
16-
}
13+
var header = new byte[8];
14+
source.ReadExactly(header, 0, 8);
15+
target.Write(pngSignature);
1716

18-
public static async Task NormalizeAsync(Stream source, Stream target, Cancel cancel)
19-
{
20-
using var buffer = new MemoryStream();
21-
await source.CopyToAsync(buffer, cancel);
22-
Normalize(buffer.GetBuffer(), (int) buffer.Length, target);
17+
using var idatData = new MemoryStream();
18+
var flushedIdat = false;
19+
20+
while (true)
21+
{
22+
source.ReadExactly(header, 0, 8);
23+
var chunkLength = BinaryPrimitives.ReadInt32BigEndian(header.AsSpan(0, 4));
24+
25+
var body = new byte[chunkLength + 4];
26+
source.ReadExactly(body, 0, body.Length);
27+
28+
if (ProcessChunk(header, body, chunkLength, target, idatData, ref flushedIdat))
29+
{
30+
break;
31+
}
32+
}
2333
}
2434

25-
static void Normalize(byte[] data, int dataLength, Stream target)
35+
public static async Task NormalizeAsync(Stream source, Stream target, Cancel cancel)
2636
{
37+
var header = new byte[8];
38+
await source.ReadExactlyAsync(header, 0, 8, cancel);
2739
target.Write(pngSignature);
2840

29-
var idatData = new MemoryStream();
30-
var preIdatChunks = new List<byte[]>();
31-
var postIdatChunks = new List<byte[]>();
32-
var seenIdat = false;
33-
var offset = 8;
41+
using var idatData = new MemoryStream();
42+
var flushedIdat = false;
3443

35-
while (offset + 12 <= dataLength)
44+
while (true)
3645
{
37-
var chunkLength = BinaryPrimitives.ReadInt32BigEndian(data.AsSpan(offset));
38-
var totalChunkSize = 4 + 4 + chunkLength + 4;
46+
await source.ReadExactlyAsync(header, 0, 8, cancel);
47+
var chunkLength = BinaryPrimitives.ReadInt32BigEndian(header.AsSpan(0, 4));
3948

40-
var isIdat = data[offset + 4] == 'I' &&
41-
data[offset + 5] == 'D' &&
42-
data[offset + 6] == 'A' &&
43-
data[offset + 7] == 'T';
49+
var body = new byte[chunkLength + 4];
50+
await source.ReadExactlyAsync(body, 0, body.Length, cancel);
4451

45-
if (isIdat)
52+
if (ProcessChunk(header, body, chunkLength, target, idatData, ref flushedIdat))
4653
{
47-
seenIdat = true;
48-
idatData.Write(data, offset + 8, chunkLength);
54+
break;
4955
}
50-
else
51-
{
52-
var rawChunk = new byte[totalChunkSize];
53-
Buffer.BlockCopy(data, offset, rawChunk, 0, totalChunkSize);
54-
55-
if (seenIdat)
56-
{
57-
postIdatChunks.Add(rawChunk);
58-
}
59-
else
60-
{
61-
preIdatChunks.Add(rawChunk);
62-
}
63-
}
64-
65-
offset += totalChunkSize;
6656
}
57+
}
6758

68-
foreach (var chunk in preIdatChunks)
59+
static bool ProcessChunk(byte[] header, byte[] body, int chunkLength, Stream target, MemoryStream idatData, ref bool flushedIdat)
60+
{
61+
var isIdat = header[4] == 'I' && header[5] == 'D' &&
62+
header[6] == 'A' && header[7] == 'T';
63+
var isIend = header[4] == 'I' && header[5] == 'E' &&
64+
header[6] == 'N' && header[7] == 'D';
65+
66+
if (isIdat)
6967
{
70-
target.Write(chunk);
68+
idatData.Write(body, 0, chunkLength);
7169
}
72-
73-
if (idatData.Length > 0)
70+
else
7471
{
75-
var zlibBytes = idatData.ToArray();
76-
77-
byte[] decompressed;
78-
using (var zlibInput = new MemoryStream(zlibBytes))
79-
using (var zlibStream = new ZLibStream(zlibInput, CompressionMode.Decompress))
80-
using (var decompressedStream = new MemoryStream())
72+
if (!flushedIdat && idatData.Length > 0)
8173
{
82-
zlibStream.CopyTo(decompressedStream);
83-
decompressed = decompressedStream.ToArray();
74+
flushedIdat = true;
75+
WriteNormalizedIdat(target, idatData);
8476
}
8577

86-
byte[] newIdatData;
87-
using (var compressOutput = new MemoryStream())
88-
{
89-
using (var zlibStream = new ZLibStream(compressOutput, CompressionLevel.Optimal, leaveOpen: true))
90-
{
91-
zlibStream.Write(decompressed);
92-
}
78+
target.Write(header);
79+
target.Write(body);
80+
}
9381

94-
newIdatData = compressOutput.ToArray();
95-
}
82+
return isIend;
83+
}
9684

97-
WriteChunk(target, idatType, newIdatData);
85+
static void WriteNormalizedIdat(Stream target, MemoryStream idatData)
86+
{
87+
var zlibBytes = idatData.ToArray();
88+
89+
byte[] decompressed;
90+
using (var zlibInput = new MemoryStream(zlibBytes))
91+
using (var zlibStream = new ZLibStream(zlibInput, CompressionMode.Decompress))
92+
using (var decompressedStream = new MemoryStream())
93+
{
94+
zlibStream.CopyTo(decompressedStream);
95+
decompressed = decompressedStream.ToArray();
9896
}
9997

100-
foreach (var chunk in postIdatChunks)
98+
byte[] newIdatData;
99+
using (var compressOutput = new MemoryStream())
101100
{
102-
target.Write(chunk);
101+
using (var zlibStream = new ZLibStream(compressOutput, CompressionLevel.Optimal, leaveOpen: true))
102+
{
103+
zlibStream.Write(decompressed);
104+
}
105+
106+
newIdatData = compressOutput.ToArray();
103107
}
108+
109+
WriteChunk(target, idatType, newIdatData);
104110
}
105111

106112
static void WriteChunk(Stream target, byte[] type, byte[] data)

src/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageVersion Include="System.IO.Hashing" Version="9.0.4" />
1616
<PackageVersion Include="System.Memory" Version="4.6.3" />
1717
<PackageVersion Include="System.Buffers" Version="4.6.1" />
18+
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
1819
<PackageVersion Include="Verify" Version="31.13.2" />
1920
<PackageVersion Include="Verify.DiffPlex" Version="3.1.2" />
2021
<PackageVersion Include="Verify.NUnit" Version="31.13.2" />

0 commit comments

Comments
 (0)