Skip to content

Commit 80af08c

Browse files
Minor improvements to compression
1 parent cad5f20 commit 80af08c

File tree

3 files changed

+100
-22
lines changed

3 files changed

+100
-22
lines changed

Common/Extensions.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -926,13 +926,10 @@ public static string GetString(this byte[] bytes, Encoding encoding = null)
926926
public static string ToMD5(this string str)
927927
{
928928
var builder = new StringBuilder(32);
929-
using (var md5Hash = MD5.Create())
929+
var data = MD5.HashData(Encoding.UTF8.GetBytes(str));
930+
for (var i = 0; i < 16; i++)
930931
{
931-
var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
932-
for (var i = 0; i < 16; i++)
933-
{
934-
builder.Append(data[i].ToStringInvariant("x2"));
935-
}
932+
builder.Append(data[i].ToStringInvariant("x2"));
936933
}
937934
return builder.ToString();
938935
}
@@ -945,13 +942,10 @@ public static string ToMD5(this string str)
945942
public static string ToSHA256(this string data)
946943
{
947944
var hash = new StringBuilder(64);
948-
using (var crypt = SHA256.Create())
945+
var crypto = SHA256.HashData(Encoding.UTF8.GetBytes(data));
946+
for (var i = 0; i < 32; i++)
949947
{
950-
var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(data));
951-
for (var i = 0; i < 32; i++)
952-
{
953-
hash.Append(crypto[i].ToStringInvariant("x2"));
954-
}
948+
hash.Append(crypto[i].ToStringInvariant("x2"));
955949
}
956950
return hash.ToString();
957951
}

Compression/Compression.cs

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,83 @@ public static async Task<Dictionary<string, string>> UnzipDataAsync(Stream strea
297297
/// <returns>The zipped file as a byte array</returns>
298298
public static byte[] ZipBytes(byte[] bytes, string zipEntryName)
299299
{
300-
using (var memoryStream = new MemoryStream())
300+
using var memoryStream = new MemoryStream();
301+
ZipBytesAsync(memoryStream, bytes, zipEntryName, null).ConfigureAwait(false).GetAwaiter().GetResult();
302+
return memoryStream.ToArray();
303+
}
304+
305+
/// <summary>
306+
/// Performs an in memory zip of the specified bytes in the target stream
307+
/// </summary>
308+
/// <param name="target">The target stream</param>
309+
/// <param name="data">The file contents in bytes to be zipped</param>
310+
/// <param name="zipEntryName">The zip entry name</param>
311+
/// <param name="compressionLevel">The desired compression level</param>
312+
/// <returns>The zipped file as a byte array</returns>
313+
public static async Task ZipBytesAsync(Stream target, byte[] data, string zipEntryName, CompressionLevel? compressionLevel = null)
314+
{
315+
await ZipBytesAsync(target, [new KeyValuePair<byte[], string>(data, zipEntryName)], compressionLevel).ConfigureAwait(false);
316+
}
317+
318+
/// <summary>
319+
/// Performs an in memory zip of the specified bytes in the target stream
320+
/// </summary>
321+
/// <param name="target">The target stream</param>
322+
/// <param name="data">The file contents in bytes to be zipped</param>
323+
/// <param name="compressionLevel">The desired compression level</param>
324+
/// <returns>The zipped file as a byte array</returns>
325+
public static async Task ZipBytesAsync(Stream target, IEnumerable<KeyValuePair<byte[], string>> data, CompressionLevel? compressionLevel = null)
326+
{
327+
using var archive = new ZipArchive(target, ZipArchiveMode.Create, true);
328+
foreach (var kvp in data)
301329
{
302-
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
330+
var entry = archive.CreateEntry(kvp.Value, compressionLevel ?? CompressionLevel.SmallestSize);
331+
using var entryStream = entry.Open();
332+
await entryStream.WriteAsync(kvp.Key).ConfigureAwait(false);
333+
}
334+
}
335+
336+
/// <summary>
337+
/// Performs an in memory zip of the specified stream in the target stream
338+
/// </summary>
339+
/// <param name="target">The target stream</param>
340+
/// <param name="data">The file contents in bytes to be zipped</param>
341+
/// <param name="mode">The archive mode</param>
342+
/// <param name="compressionLevel">The desired compression level</param>
343+
/// <returns>The zipped file as a byte array</returns>
344+
public static async Task ZipStreamsAsync(string target, IEnumerable<KeyValuePair<string, Stream>> data, ZipArchiveMode mode = ZipArchiveMode.Create,
345+
CompressionLevel? compressionLevel = null)
346+
{
347+
using var fileStream = mode == ZipArchiveMode.Update
348+
? new FileStream(target, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)
349+
: new FileStream(target, FileMode.Create, FileAccess.Write, FileShare.None);
350+
await ZipStreamsAsync(fileStream, data, mode, compressionLevel).ConfigureAwait(false);
351+
}
352+
353+
/// <summary>
354+
/// Performs an in memory zip of the specified stream in the target stream
355+
/// </summary>
356+
/// <param name="target">The target stream</param>
357+
/// <param name="data">The file contents in bytes to be zipped</param>
358+
/// <param name="mode">The archive mode</param>
359+
/// <param name="compressionLevel">The desired compression level</param>
360+
/// <param name="leaveStreamOpen">True to leave the taget stream open</param>
361+
/// <returns>The zipped file as a byte array</returns>
362+
public static async Task ZipStreamsAsync(Stream target, IEnumerable<KeyValuePair<string, Stream>> data, ZipArchiveMode mode = ZipArchiveMode.Create,
363+
CompressionLevel? compressionLevel = null, bool leaveStreamOpen = false)
364+
{
365+
compressionLevel ??= CompressionLevel.SmallestSize;
366+
using var archive = new ZipArchive(target, mode, leaveStreamOpen);
367+
foreach (var kvp in data)
368+
{
369+
if (archive.Mode == ZipArchiveMode.Update)
303370
{
304-
var entry = archive.CreateEntry(zipEntryName);
305-
using (var entryStream = entry.Open())
306-
{
307-
entryStream.Write(bytes, 0, bytes.Length);
308-
}
371+
var existingEntry = archive.GetEntry(kvp.Key);
372+
existingEntry?.Delete();
309373
}
310-
// 'ToArray' after disposing of 'ZipArchive' since it finishes writing all the data
311-
return memoryStream.ToArray();
374+
var entry = archive.CreateEntry(kvp.Key, compressionLevel.Value);
375+
using var entryStream = entry.Open();
376+
await kvp.Value.CopyToAsync(entryStream).ConfigureAwait(false);
312377
}
313378
}
314379

Tests/Compression/CompressionTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ public void ReadLinesCountMatchesLineCount()
3838
Assert.AreEqual(expected, actual);
3939
}
4040

41+
[Test]
42+
public void ZipsStream()
43+
{
44+
const string zipName = "stream_entry.zip";
45+
File.Delete(zipName);
46+
const string fileContents = "this is the contents of a file!";
47+
using var memoryStream = new MemoryStream();
48+
var array = Encoding.UTF8.GetBytes(fileContents);
49+
memoryStream.Write(array);
50+
memoryStream.Position = 0;
51+
52+
QuantConnect.Compression.ZipStreamsAsync(zipName, [new ("entry", memoryStream)]).Wait();
53+
54+
using var file = File.OpenRead(zipName);
55+
using var streamReader = QuantConnect.Compression.UnzipStreamToStreamReader(file);
56+
var contents = streamReader.ReadToEnd();
57+
Assert.AreEqual(fileContents, contents);
58+
}
59+
4160
[Test]
4261
public void ZipBytes()
4362
{
@@ -61,7 +80,7 @@ public void ZipBytesReturnsByteArrayWithCorrectLength()
6180
var fileBytes = File.ReadAllBytes(file);
6281
var zippedBytes = QuantConnect.Compression.ZipBytes(fileBytes, "entry");
6382

64-
Assert.AreEqual(OS.IsWindows ? 905921 : 906121, zippedBytes.Length);
83+
Assert.AreEqual(OS.IsWindows ? 905693 : 905693, zippedBytes.Length);
6584
}
6685

6786
[Test]

0 commit comments

Comments
 (0)