2021-04-29 15:35:57 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
using System;
|
|
|
|
|
using System.Buffers.Binary;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
2021-04-29 15:10:34 -04:00
|
|
|
using EpicGames.Core;
|
|
|
|
|
using ICSharpCode.SharpZipLib.BZip2;
|
2021-08-07 13:08:14 -04:00
|
|
|
using OpenTracing;
|
|
|
|
|
using OpenTracing.Util;
|
2021-04-29 15:10:34 -04:00
|
|
|
|
2022-03-16 11:18:39 -04:00
|
|
|
namespace Horde.Build.Utilities
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Extension methods for compression
|
|
|
|
|
/// </summary>
|
|
|
|
|
static class CompressionExtensions
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compress a block of data with bzip2
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The compressed data</returns>
|
2022-03-23 14:50:23 -04:00
|
|
|
public static byte[] CompressBzip2(this ReadOnlyMemory<byte> memory)
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
using IScope scope = GlobalTracer.Instance.BuildSpan("CompressBzip2").StartActive();
|
|
|
|
|
scope.Span.SetTag("DecompressedSize", memory.Length.ToString(CultureInfo.InvariantCulture));
|
2021-04-29 15:10:34 -04:00
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
byte[] compressedData;
|
|
|
|
|
using (MemoryStream stream = new MemoryStream())
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
byte[] decompressedSize = new byte[4];
|
|
|
|
|
BinaryPrimitives.WriteInt32LittleEndian(decompressedSize.AsSpan(), memory.Length);
|
|
|
|
|
stream.Write(decompressedSize.AsSpan());
|
2021-04-29 15:10:34 -04:00
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
using (BZip2OutputStream compressedStream = new BZip2OutputStream(stream))
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
compressedStream.Write(memory.Span);
|
2021-04-29 15:10:34 -04:00
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
compressedData = stream.ToArray();
|
2021-04-29 15:10:34 -04:00
|
|
|
}
|
|
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
scope.Span.SetTag("CompressedSize", compressedData.Length.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
return compressedData;
|
2021-04-29 15:10:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Decompress the data
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The decompressed data</returns>
|
2022-03-23 14:50:23 -04:00
|
|
|
public static byte[] DecompressBzip2(this ReadOnlyMemory<byte> memory)
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
int decompressedSize = BinaryPrimitives.ReadInt32LittleEndian(memory.Span);
|
2021-04-29 15:10:34 -04:00
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
using IScope scope = GlobalTracer.Instance.BuildSpan("DecompressBzip2").StartActive();
|
|
|
|
|
scope.Span.SetTag("CompressedSize", memory.Length.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
scope.Span.SetTag("DecompressedSize", decompressedSize.ToString(CultureInfo.InvariantCulture));
|
2021-04-29 15:10:34 -04:00
|
|
|
|
2022-03-23 14:50:23 -04:00
|
|
|
byte[] data = new byte[decompressedSize];
|
|
|
|
|
using (ReadOnlyMemoryStream stream = new ReadOnlyMemoryStream(memory.Slice(4)))
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
using (BZip2InputStream decompressedStream = new BZip2InputStream(stream))
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
int readSize = decompressedStream.Read(data.AsSpan());
|
|
|
|
|
if (readSize != data.Length)
|
2021-04-29 15:10:34 -04:00
|
|
|
{
|
2022-03-23 14:50:23 -04:00
|
|
|
throw new InvalidDataException($"Compressed data is too short (expected {data.Length} bytes, got {readSize} bytes)");
|
2021-04-29 15:10:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-23 14:50:23 -04:00
|
|
|
return data;
|
2021-04-29 15:10:34 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|