diff --git a/CMakeLists.txt b/CMakeLists.txt index 1494c29..6775d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ endif() SET (ORIGINAL_LIB_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) SET(BOOST_COMPONENTS) -LIST(APPEND BOOST_COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context locale) +LIST(APPEND BOOST_COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context locale iostreams) IF( WIN32 ) MESSAGE(STATUS "Configuring fc to build on Win32") @@ -237,6 +237,9 @@ target_link_libraries( udt_server fc udt ) add_executable( udt_client tests/udtc.cpp ) target_link_libraries( udt_client fc udt ) +add_executable( lzma_compress_file tests/lzma_compress_file.cpp ) +target_link_libraries( lzma_compress_file fc ) + #add_executable( test_compress tests/compress.cpp ) #target_link_libraries( test_compress fc ) #add_executable( test_aes tests/aes_test.cpp ) diff --git a/include/fc/compress/lzma.hpp b/include/fc/compress/lzma.hpp index a6d4f81..e1b411c 100644 --- a/include/fc/compress/lzma.hpp +++ b/include/fc/compress/lzma.hpp @@ -1,9 +1,16 @@ #pragma once + +#include #include namespace fc { - std::vector lzma_compress( const std::vector& in ); - std::vector lzma_decompress( const std::vector& compressed ); +std::vector lzma_compress( const std::vector& in ); +std::vector lzma_decompress( const std::vector& compressed ); + +void lzma_compress_file( path src_path, + path dst_path, + unsigned char level = 5, + unsigned int dict_size = (1 << 20) ); } // namespace fc diff --git a/src/compress/lzma.cpp b/src/compress/lzma.cpp index 60db130..91d5475 100644 --- a/src/compress/lzma.cpp +++ b/src/compress/lzma.cpp @@ -3,6 +3,14 @@ #include +#include + +#include + + + +#include + namespace fc { std::vector lzma_compress(const std::vector& in) @@ -50,4 +58,141 @@ std::vector lzma_decompress( const std::vector& compressed ) return out; } +struct lzma_file_ctx +{ + const unsigned char* src_buf; + size_t src_len; + + path dst_path; +}; + +static int input_callback( void* input_ctx, void* input_buf, size_t* input_len ) +{ + FC_ASSERT( input_ctx != NULL ); + FC_ASSERT( input_buf != NULL ); + + const auto ctx = ( struct lzma_file_ctx* )input_ctx; + const auto size = ( ctx->src_len < *input_len ) ? ctx->src_len : *input_len; + + if( size > 0 ) + { + memcpy( input_buf, ( void * )ctx->src_buf, size ); + ctx->src_buf += size; + ctx->src_len -= size; + } + + *input_len = size; + + return 0; +} + +static size_t output_callback( void* output_ctx, const void* output_buf, size_t output_len ) +{ + FC_ASSERT( output_ctx != NULL ); + FC_ASSERT( output_buf != NULL ); + + const auto ctx = ( struct lzma_file_ctx* )output_ctx; + + if( output_len > 0 ) + { + size_t dst_len = 0; + if( !exists( ctx->dst_path ) ) + { + auto fs = std::ofstream( ctx->dst_path.string() ); + fs.close(); + } + else + { + dst_len = file_size( ctx->dst_path ); + } + + resize_file( ctx->dst_path, dst_len + output_len ); + + boost::iostreams::mapped_file_sink dst_file; + dst_file.open( ctx->dst_path.string() ); + FC_ASSERT( dst_file.is_open() ); + + memcpy( ( void* )(dst_file.data() + dst_len), output_buf, output_len); + + dst_file.close(); + } + + return output_len; +} + +void lzma_compress_file( path src_path, + path dst_path, + unsigned char level, + unsigned int dict_size ) +{ + FC_ASSERT( exists( src_path ) ); + FC_ASSERT( !exists( dst_path ) ); + + boost::iostreams::mapped_file_source src_file; + src_file.open( src_path.string() ); + FC_ASSERT( src_file.is_open() ); + + elzma_compress_handle handle = NULL; + handle = elzma_compress_alloc(); + FC_ASSERT( handle != NULL ); + + struct lzma_file_ctx ctx; + ctx.src_buf = ( const unsigned char* )src_file.data(); + ctx.src_len = src_file.size(); + ctx.dst_path = dst_path; + + auto rc = elzma_compress_config( handle, + ELZMA_LC_DEFAULT, + ELZMA_LP_DEFAULT, + ELZMA_PB_DEFAULT, + level, + dict_size, + elzma_file_format::ELZMA_lzma, + ctx.src_len ); + + try + { + FC_ASSERT( rc == ELZMA_E_OK ); + } + catch( ... ) + { + elzma_compress_free( &handle ); + throw; + } + + rc = elzma_compress_run( handle, + input_callback, + ( void * )&ctx, + output_callback, + ( void * )&ctx, + NULL, + NULL ); + + elzma_compress_free( &handle ); + FC_ASSERT( rc == ELZMA_E_OK ); + + + /* TEST */ + FC_ASSERT( exists( dst_path ) ); + + boost::iostreams::mapped_file_source dst_file; + dst_file.open( dst_path.string() ); + FC_ASSERT( dst_file.is_open() ); + + std::vector result( dst_file.data(), dst_file.data() + dst_file.size() ); + dst_file.close(); + + for( const auto& c : result ) + { + std::cout << c; + } + std::cout << "\n"; + + result = lzma_decompress( result ); + for( const auto& c : result ) + { + std::cout << c; + } +} + } // namespace fc diff --git a/tests/lzma_compress_file.cpp b/tests/lzma_compress_file.cpp new file mode 100644 index 0000000..3f76733 --- /dev/null +++ b/tests/lzma_compress_file.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include +#include + +using namespace fc; + +int main( int argc, char** argv ) +{ + if( argc != 2 && argc != 3 ) + { + std::cout << "usage: " << argv[0] << " [dst_path = src_path.lzma]\n"; + exit( -1 ); + } + + auto src = std::string( argv[1] ); + auto dst = (argc == 3) ? std::string( argv[2] ) : src + ".lzma"; + + lzma_compress_file( src, dst ); + + return 0; +}