summaryrefslogblamecommitdiffstats
path: root/src/StringCompression.cpp
blob: d82f45987fe985c6d934ccbbad7b43d110b0a4b1 (plain) (tree)
1
2
3
4
5
6
7
8
9


                        
                                                                      

                    
                       
                              
 
                       



 

                                                           
 











                                                                              
         













                                                                                           





 
                                                          
 


                                                                  
         
                                       
         




 
 
                                      
 



                                             
 
 
 



                                                                                                  
         
                                                                    





                                                                                                            
         
 




                                                                                                                
         



                                                                                                                  
                 

                                                                       
 


                                     
 






                                                                                               





 
                                                                                               
 

                                                                               
 
 
 















                                                                                                      
         
                                       
         
 
 
 
 






















                                                                                       




 
 
                                                                                                                
 










                                                                                         
         
                                                                    







                                                                                                                        
         
 



                                                                                                                
         



                                                                                                                          
                 

                                                                                                                   
                         

                                                     
                         



                                                                                     
 
 


 




















                                                                                                                                                          
 

                                                            

// StringCompression.cpp

// Implements the wrapping functions for compression and decompression

#include "Globals.h"
#include "ByteBuffer.h"
#include "StringCompression.h"

#include <libdeflate.h>





std::string_view Compression::Result::GetStringView() const
{
	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
	{
		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
	};
}





Compression::Compressor::Compressor(int CompressionFactor)
{
	m_Handle = libdeflate_alloc_compressor(CompressionFactor);

	if (m_Handle == nullptr)
	{
		throw std::bad_alloc();
	}
}





Compression::Compressor::~Compressor()
{
	libdeflate_free_compressor(m_Handle);
}





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:
	{
		Result::Static Buffer = {static_cast<std::byte>(0)};
		const auto BytesWrittenOut = Algorithm(m_Handle, Input, Size, Buffer.data(), Buffer.size());

		if (BytesWrittenOut != 0)
		{
			return { Buffer, BytesWrittenOut };
		}
	}

	// 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)
	{
		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)
		{
			return { std::move(Dynamic), BytesWrittenOut };
		}

		DynamicCapacity *= 2;
	}
}





Compression::Result Compression::Compressor::CompressGZip(const ContiguousByteBufferView Input)
{
	return Compress<&libdeflate_gzip_compress>(Input.data(), Input.size());
}





Compression::Result Compression::Compressor::CompressZLib(const ContiguousByteBufferView Input)
{
	return Compress<&libdeflate_zlib_compress>(Input.data(), Input.size());
}





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)
	{
		throw std::bad_alloc();
	}
}





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);
}





Compression::Result Compression::Extractor::ExtractZLib(ContiguousByteBufferView Input, size_t UncompressedSize)
{
	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:
	{
		Result::Static Buffer = {static_cast<std::byte>(0)};
		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.");
		}
	}

	// 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)
	{
		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))
		{
			case libdeflate_result::LIBDEFLATE_SUCCESS: return { std::move(Dynamic), BytesWrittenOut };
			case libdeflate_result::LIBDEFLATE_INSUFFICIENT_SPACE:
			{
				DynamicCapacity *= 2;
				continue;
			}
			default: throw std::runtime_error("Data extraction failed.");
		}
	}
}





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 };
	}

	throw std::runtime_error("Data extraction failed.");
}