2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
// StringCompression.cpp
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
// Implements the wrapping functions for compression and decompression
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
#include "Globals.h"
|
2021-01-11 11:39:43 -05:00
|
|
|
#include "ByteBuffer.h"
|
2012-06-14 09:06:06 -04:00
|
|
|
#include "StringCompression.h"
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
#include <libdeflate.h>
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
|
|
|
|
std::string_view Compression::Result::GetStringView() const
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
const auto View = GetView();
|
|
|
|
return { reinterpret_cast<const char *>(View.data()), View.size() };
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ContiguousByteBufferView Compression::Result::GetView() const
|
|
|
|
{
|
|
|
|
// Get a generic std::byte * to what the variant is currently storing:
|
|
|
|
return
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
std::visit([](const auto & Buffer) -> const std::byte *
|
|
|
|
{
|
|
|
|
using Variant = std::decay_t<decltype(Buffer)>;
|
|
|
|
|
|
|
|
if constexpr (std::is_same_v<Variant, Compression::Result::Static>)
|
|
|
|
{
|
|
|
|
return Buffer.data();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return Buffer.get();
|
|
|
|
}
|
|
|
|
}, Storage), Size
|
|
|
|
};
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
Compression::Compressor::Compressor(int CompressionFactor)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Handle = libdeflate_alloc_compressor(CompressionFactor);
|
|
|
|
|
|
|
|
if (m_Handle == nullptr)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::bad_alloc();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-06-02 06:40:20 -04:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
Compression::Compressor::~Compressor()
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
libdeflate_free_compressor(m_Handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-02 06:40:20 -04:00
|
|
|
|
|
|
|
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
template <auto Algorithm>
|
|
|
|
Compression::Result Compression::Compressor::Compress(const void * const Input, const size_t Size)
|
|
|
|
{
|
|
|
|
// First see if the stack buffer has enough space:
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
Result::Static Buffer;
|
|
|
|
const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Buffer.data(), Buffer.size());
|
|
|
|
|
|
|
|
if (BytesWrittenOut != 0)
|
|
|
|
{
|
|
|
|
return { Buffer, BytesWrittenOut };
|
|
|
|
}
|
2013-06-02 06:40:20 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
// No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2.
|
|
|
|
// This will either succeed, or except with bad_alloc.
|
|
|
|
|
|
|
|
auto DynamicCapacity = Result::StaticCapacity * 2;
|
|
|
|
while (true)
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity);
|
|
|
|
const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Dynamic.get(), DynamicCapacity);
|
|
|
|
|
|
|
|
if (BytesWrittenOut != 0)
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
return { std::move(Dynamic), BytesWrittenOut };
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
DynamicCapacity *= 2;
|
|
|
|
}
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compression::Result Compression::Compressor::CompressGZip(const ContiguousByteBufferView Input)
|
|
|
|
{
|
|
|
|
return Compress<&libdeflate_gzip_compress>(Input.data(), Input.size());
|
2013-06-02 06:40:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
Compression::Result Compression::Compressor::CompressZLib(const ContiguousByteBufferView Input)
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
return Compress<&libdeflate_zlib_compress>(Input.data(), Input.size());
|
|
|
|
}
|
2013-06-02 06:40:20 -04:00
|
|
|
|
|
|
|
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
Compression::Result Compression::Compressor::CompressZLib(const void * const Input, const size_t Size)
|
|
|
|
{
|
|
|
|
return Compress<&libdeflate_zlib_compress>(Input, Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compression::Extractor::Extractor()
|
|
|
|
{
|
|
|
|
m_Handle = libdeflate_alloc_decompressor();
|
|
|
|
|
|
|
|
if (m_Handle == nullptr)
|
2013-06-02 06:40:20 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::bad_alloc();
|
2013-06-02 06:40:20 -04:00
|
|
|
}
|
2021-01-11 11:39:43 -05:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
|
|
|
|
|
|
|
|
Compression::Extractor::~Extractor()
|
|
|
|
{
|
|
|
|
libdeflate_free_decompressor(m_Handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compression::Result Compression::Extractor::ExtractGZip(ContiguousByteBufferView Input)
|
|
|
|
{
|
|
|
|
return Extract<&libdeflate_gzip_decompress>(Input);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input)
|
|
|
|
{
|
|
|
|
return Extract<&libdeflate_zlib_decompress>(Input);
|
2013-06-02 06:40:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-09-03 13:36:53 -04:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input, size_t UncompressedSize)
|
2014-09-03 13:36:53 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
return Extract<&libdeflate_zlib_decompress>(Input, UncompressedSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <auto Algorithm>
|
|
|
|
Compression::Result Compression::Extractor::Extract(const ContiguousByteBufferView Input)
|
|
|
|
{
|
|
|
|
// First see if the stack buffer has enough space:
|
2014-09-03 13:36:53 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
Result::Static Buffer;
|
|
|
|
size_t BytesWrittenOut;
|
|
|
|
|
|
|
|
switch (Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), Buffer.size(), &BytesWrittenOut))
|
|
|
|
{
|
|
|
|
case LIBDEFLATE_SUCCESS: return { Buffer, BytesWrittenOut };
|
|
|
|
case LIBDEFLATE_INSUFFICIENT_SPACE: break;
|
|
|
|
default: throw std::runtime_error("Data extraction failed.");
|
|
|
|
}
|
2014-09-03 13:36:53 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
// No it doesn't. Allocate space on the heap to write the compression result, increasing in powers of 2.
|
|
|
|
|
|
|
|
auto DynamicCapacity = Result::StaticCapacity * 2;
|
|
|
|
while (true)
|
2014-09-03 13:36:53 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
size_t BytesWrittenOut;
|
|
|
|
auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(DynamicCapacity);
|
|
|
|
|
|
|
|
switch (Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), DynamicCapacity, &BytesWrittenOut))
|
2014-09-03 13:36:53 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
case libdeflate_result::LIBDEFLATE_SUCCESS: return { std::move(Dynamic), BytesWrittenOut };
|
|
|
|
case libdeflate_result::LIBDEFLATE_INSUFFICIENT_SPACE:
|
2014-09-03 13:36:53 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
DynamicCapacity *= 2;
|
|
|
|
continue;
|
2014-09-03 13:36:53 -04:00
|
|
|
}
|
2021-01-11 11:39:43 -05:00
|
|
|
default: throw std::runtime_error("Data extraction failed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
|
|
|
|
2014-09-03 13:36:53 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
template <auto Algorithm>
|
|
|
|
Compression::Result Compression::Extractor::Extract(const ContiguousByteBufferView Input, size_t UncompressedSize)
|
|
|
|
{
|
|
|
|
// Here we have the expected size after extraction, so directly use a suitable buffer size:
|
|
|
|
if (UncompressedSize <= Result::StaticCapacity)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
Result::Static Buffer;
|
|
|
|
Algorithm(m_Handle, Input.data(), Input.size(), Buffer.data(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return { Buffer, UncompressedSize };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (
|
|
|
|
auto Dynamic = cpp20::make_unique_for_overwrite<Result::Dynamic::element_type[]>(UncompressedSize);
|
|
|
|
Algorithm(m_Handle, Input.data(), Input.size(), Dynamic.get(), UncompressedSize, nullptr) == libdeflate_result::LIBDEFLATE_SUCCESS
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return { std::move(Dynamic), UncompressedSize };
|
|
|
|
}
|
2014-09-03 13:36:53 -04:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error("Data extraction failed.");
|
|
|
|
}
|