aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.Compression.Zip/CrcStream.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.Compression.Zip/CrcStream.cs250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.Compression.Zip/CrcStream.cs b/src/dtf/WixToolset.Dtf.Compression.Zip/CrcStream.cs
new file mode 100644
index 00000000..c645ecc1
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Compression.Zip/CrcStream.cs
@@ -0,0 +1,250 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Compression.Zip
4{
5 using System;
6 using System.IO;
7 using System.Diagnostics.CodeAnalysis;
8
9 /// <summary>
10 /// Wraps a source stream and calcaluates a CRC over all bytes that are read or written.
11 /// </summary>
12 /// <remarks>
13 /// The CRC algorithm matches that used in the standard ZIP file format.
14 /// </remarks>
15 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
16 public class CrcStream : Stream
17 {
18 private Stream source;
19 private uint crc;
20
21 /// <summary>
22 /// Creates a new CrcStream instance from a source stream.
23 /// </summary>
24 /// <param name="source">Underlying stream where bytes will be read from or written to.</param>
25 public CrcStream(Stream source)
26 {
27 this.source = source;
28 }
29
30 /// <summary>
31 /// Gets the current CRC over all bytes that have been read or written
32 /// since this instance was created.
33 /// </summary>
34 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
35 public uint Crc
36 {
37 get
38 {
39 return this.crc;
40 }
41 }
42
43 /// <summary>
44 /// Gets the underlying stream that this stream reads from or writes to.
45 /// </summary>
46 public Stream Source
47 {
48 get
49 {
50 return this.source;
51 }
52 }
53
54 /// <summary>
55 /// Gets a value indicating whether the source stream supports reading.
56 /// </summary>
57 /// <value>true if the stream supports reading; otherwise, false.</value>
58 public override bool CanRead
59 {
60 get
61 {
62 return this.source.CanRead;
63 }
64 }
65
66 /// <summary>
67 /// Gets a value indicating whether the source stream supports writing.
68 /// </summary>
69 /// <value>true if the stream supports writing; otherwise, false.</value>
70 public override bool CanWrite
71 {
72 get
73 {
74 return this.source.CanWrite;
75 }
76 }
77
78 /// <summary>
79 /// Gets a value indicating whether the source stream supports seeking.
80 /// </summary>
81 /// <value>true if the stream supports seeking; otherwise, false.</value>
82 public override bool CanSeek
83 {
84 get
85 {
86 return this.source.CanSeek;
87 }
88 }
89
90 /// <summary>
91 /// Gets the length of the source stream.
92 /// </summary>
93 public override long Length
94 {
95 get
96 {
97 return this.source.Length;
98 }
99 }
100
101 /// <summary>
102 /// Gets or sets the position of the source stream.
103 /// </summary>
104 public override long Position
105 {
106 get
107 {
108 return this.source.Position;
109 }
110
111 set
112 {
113 this.source.Position = value;
114 }
115 }
116
117 /// <summary>
118 /// Sets the position within the source stream.
119 /// </summary>
120 /// <param name="offset">A byte offset relative to the origin parameter.</param>
121 /// <param name="origin">A value of type SeekOrigin indicating
122 /// the reference point used to obtain the new position.</param>
123 /// <returns>The new position within the source stream.</returns>
124 /// <remarks>
125 /// Note the CRC is only calculated over bytes that are actually read or
126 /// written, so any bytes skipped by seeking will not contribute to the CRC.
127 /// </remarks>
128 public override long Seek(long offset, SeekOrigin origin)
129 {
130 return this.source.Seek(offset, origin);
131 }
132
133 /// <summary>
134 /// Sets the length of the source stream.
135 /// </summary>
136 /// <param name="value">The desired length of the
137 /// stream in bytes.</param>
138 public override void SetLength(long value)
139 {
140 this.source.SetLength(value);
141 }
142
143 /// <summary>
144 /// Reads a sequence of bytes from the source stream and advances
145 /// the position within the stream by the number of bytes read.
146 /// </summary>
147 /// <param name="buffer">An array of bytes. When this method returns, the buffer
148 /// contains the specified byte array with the values between offset and
149 /// (offset + count - 1) replaced by the bytes read from the current source.</param>
150 /// <param name="offset">The zero-based byte offset in buffer at which to begin
151 /// storing the data read from the current stream.</param>
152 /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
153 /// <returns>The total number of bytes read into the buffer. This can be less
154 /// than the number of bytes requested if that many bytes are not currently available,
155 /// or zero (0) if the end of the stream has been reached.</returns>
156 public override int Read(byte[] buffer, int offset, int count)
157 {
158 count = this.source.Read(buffer, offset, count);
159 this.UpdateCrc(buffer, offset, count);
160 return count;
161 }
162
163 /// <summary>
164 /// Writes a sequence of bytes to the source stream and advances the
165 /// current position within this stream by the number of bytes written.
166 /// </summary>
167 /// <param name="buffer">An array of bytes. This method copies count
168 /// bytes from buffer to the current stream.</param>
169 /// <param name="offset">The zero-based byte offset in buffer at which
170 /// to begin copying bytes to the current stream.</param>
171 /// <param name="count">The number of bytes to be written to the
172 /// current stream.</param>
173 public override void Write(byte[] buffer, int offset, int count)
174 {
175 this.source.Write(buffer, offset, count);
176 this.UpdateCrc(buffer, offset, count);
177 }
178
179 /// <summary>
180 /// Flushes the source stream.
181 /// </summary>
182 public override void Flush()
183 {
184 this.source.Flush();
185 }
186
187 /// <summary>
188 /// Closes the underlying stream.
189 /// </summary>
190 public override void Close()
191 {
192 this.source.Close();
193 base.Close();
194 }
195
196 /// <summary>
197 /// Updates the CRC with a range of bytes that were read or written.
198 /// </summary>
199 private void UpdateCrc(byte[] buffer, int offset, int count)
200 {
201 this.crc = ~this.crc;
202 for( ; count > 0; count--, offset++)
203 {
204 this.crc = (this.crc >> 8) ^
205 CrcStream.crcTable[(this.crc & 0xFF) ^ buffer[offset]];
206 }
207 this.crc = ~this.crc;
208 }
209
210 private static uint[] crcTable = MakeCrcTable();
211
212 /// <summary>
213 /// Computes a table that speeds up calculation of the CRC.
214 /// </summary>
215 private static uint[] MakeCrcTable()
216 {
217 const uint poly = 0x04C11DB7u;
218 uint[] crcTable = new uint[256];
219 for(uint n = 0; n < 256; n++)
220 {
221 uint c = CrcStream.Reflect(n, 8);
222 c = c << 24;
223 for(uint k = 0; k < 8; k++)
224 {
225 c = (c << 1) ^ ((c & 0x80000000u) != 0 ? poly : 0);
226 }
227 crcTable[n] = CrcStream.Reflect(c, 32);
228 }
229 return crcTable;
230 }
231
232 /// <summary>
233 /// Reflects the ordering of certain number of bits. For exmample when reflecting
234 /// one byte, bit one is swapped with bit eight, bit two with bit seven, etc.
235 /// </summary>
236 private static uint Reflect(uint value, int bits)
237 {
238 for (int i = 0; i < bits / 2; i++)
239 {
240 uint leftBit = 1u << (bits - 1 - i);
241 uint rightBit = 1u << i;
242 if (((value & leftBit) != 0) != ((value & rightBit) != 0))
243 {
244 value ^= leftBit | rightBit;
245 }
246 }
247 return value;
248 }
249 }
250}