ss_bsdiff: Add Brotli compression method 10/271110/5
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 14 Feb 2022 17:14:32 +0000 (18:14 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 21 Feb 2022 14:59:47 +0000 (15:59 +0100)
ss_bsdiff can produce patches compressed with both LZMA (default) and
Brotli method.
Use the '-c <lzma|brotli>' to select appropriate method.

Change-Id: I05b6c1cf22826530f823b72940af8d8ff7f602c9

bsdiff/CMakeLists.txt
bsdiff/ss_bsdiff.c

index 497299c..ecd7205 100755 (executable)
@@ -10,7 +10,7 @@ SET(ss_bspatch_SRCS
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/bsdiff)
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED liblzma-tool libdivsufsort)
+pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED liblzma-tool libdivsufsort libbrotlienc)
 
 FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
index 13ef124..209f2e9 100755 (executable)
 #define _CRT_SECURE_NO_WARNINGS
 #include <assert.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
 #include <err.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -45,6 +48,7 @@
 #include <7zVersion.h>
 #include <LzmaDec.h>
 #include <LzmaEnc.h>
+#include <brotli/encode.h>
 
 #define SUFSORT_MOD                                    // Change suffix sorting algorithm from Qsufsort to Divsufsort
 //#define ZLIB_MOD                                     // Change compression algorithm
@@ -55,6 +59,7 @@
 #define MULTI_THREADING 1            // only with #define CONST_MEMORY_USAGE or #define MAX_MATCH_SIZE
 #define TIME_LIMIT_CHECK 300
 #define TEMP_PATCH_NAME "temp_patch"
+#define BROTLI_COMPRESSION_QUALITY 9
 
 /* Take care :
 1) Use either (MAX_MATCH_SIZE or CONST_MEMORY_USAGE) or (none of both).
@@ -110,6 +115,7 @@ struct data_thread {
 
 enum compression_method {
        CM_LZMA,
+       CM_BROTLI,
 };
 
 struct bsdiff_info {
@@ -526,7 +532,8 @@ static SRes lzma_encode(ISeqOutStream *outStream, ISeqInStream *inStream, UInt64
 
 int lzma_compress(const char *input_file, const char *output_file, char *rs, int rs_size)
 {
-       assert(patch_file);
+       assert(input_file);
+       assert(output_file);
        assert(rs);
 
        CFileSeqInStream inStream;
@@ -573,10 +580,96 @@ int lzma_compress(const char *input_file, const char *output_file, char *rs, int
        return 0;
 }
 
+int brotli_compress_internal(int input_fd, int output_fd, int quality)
+{
+       int res = -1;
+       size_t input_size = lseek(input_fd, 0, SEEK_END);
+       lseek(input_fd, 0, SEEK_SET);
+       void *input_file_ptr = mmap(NULL, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
+
+       if (input_file_ptr == MAP_FAILED) {
+               printf("Can not mmap input file: %d - %m\n", errno);
+               goto exit;
+       }
+
+       BrotliEncoderState *bstate = BrotliEncoderCreateInstance(NULL, NULL, NULL);
+       if (bstate == 0) {
+               printf("Can not create BrotliEncoder instance\n");
+               goto exit;
+       }
+       size_t max_output_size = BrotliEncoderMaxCompressedSize(input_size);
+
+       if (max_output_size == 0) {
+               printf("Brotli engine error\n");
+               goto exit;
+       }
+
+       if (ftruncate(output_fd, max_output_size) == -1) {
+               printf("Can not truncate output file: %d - %m\n", errno);
+               goto exit;
+       }
+
+       void *output_file_ptr = mmap(NULL, max_output_size, PROT_WRITE, MAP_SHARED, output_fd, 0);
+       if (output_file_ptr == MAP_FAILED) {
+               printf("Can not mmap output file: %d - %m\n", errno);
+               goto exit;
+       }
+
+       if(!BrotliEncoderCompress(quality,
+                                 BROTLI_DEFAULT_WINDOW,
+                                 BROTLI_DEFAULT_MODE,
+                                 input_size,
+                                 input_file_ptr,
+                                 &max_output_size,
+                                 output_file_ptr)) {
+               printf("Compression error\n");
+               goto exit;
+       }
+       if (ftruncate(output_fd, max_output_size) == -1) {
+               printf("Can not truncate output file after compression: %d - %m\n", errno);
+               goto exit;
+       }
+
+       res = 0;
+exit:
+       if (input_file_ptr)
+               munmap(input_file_ptr, input_size);
+       if (output_file_ptr)
+               munmap(output_file_ptr, max_output_size);
+
+       return res;
+}
+
+int brotli_compress(const char *input_file, const char *output_file, int quality)
+{
+       assert(input_file);
+       assert(output_file);
+       int res = -1;
+
+       int input_fd = open(input_file, O_RDONLY);
+       if (input_fd < 0) {
+               printf("Can not open file: %s for read\n", input_file);
+               return res;
+       }
+       int output_fd = open(output_file, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
+       if (output_fd < 0) {
+               printf("Can not open file: %s for write (%d: %m)\n", output_file, errno);
+               close(input_fd);
+               return res;
+       }
+
+       res = brotli_compress_internal(input_fd, output_fd, quality);
+
+       close(input_fd);
+       close(output_fd);
+
+       return res;
+}
+
 void print_help(const char *arg0)
 {
        assert(arg0);
-       errx(1, "ss_bsdiff Version 5.0\nUsage: %s oldfile newfile patchfile\n", arg0);
+       errx(1, "ss_bsdiff Version 5.0\nUsage: %s [-c <lzma|brotli>] oldfile newfile patchfile\n", arg0);
 }
 
 int parse_args(struct bsdiff_info *info, int argc, char *argv[])
@@ -584,6 +677,8 @@ int parse_args(struct bsdiff_info *info, int argc, char *argv[])
        assert(info);
        assert(argv);
 
+       info->comp_method = CM_LZMA; // default compression method
+
        struct option long_options[] = {
                {"compression", optional_argument, NULL, 'c'},
                {0, 0 , 0, 0}
@@ -596,6 +691,8 @@ int parse_args(struct bsdiff_info *info, int argc, char *argv[])
                        case 'c':
                                if (strcmp("lzma", optarg) == 0)
                                        info->comp_method = CM_LZMA;
+                               else if (strcmp("brotli", optarg) == 0)
+                                       info->comp_method = CM_BROTLI;
                                else {
                                        err(1, "Unknown compression method: %s", optarg);
                                        return -1;
@@ -637,7 +734,20 @@ int MY_CDECL main(int argc, char *argv[])
        if (ret != 0)
                err(1, "bsdiff fails to create delta within timelimit");
 #endif
-       int res = lzma_compress(TEMP_PATCH_NAME, info.patch_file, rs, sizeof(rs));
+       int res = 0;
+       switch(info.comp_method) {
+               case CM_LZMA:
+                       res = lzma_compress(TEMP_PATCH_NAME, info.patch_file, rs, sizeof(rs));
+                       break;
+               case CM_BROTLI:
+                       res = brotli_compress(TEMP_PATCH_NAME, info.patch_file, BROTLI_COMPRESSION_QUALITY);
+                       break;
+               default:
+                       printf("Unknown compression method\n");
+                       res = -1;
+                       break;
+       }
+
        if (remove(TEMP_PATCH_NAME) < 0)
                printf("Failed to remove %s\n", TEMP_PATCH_NAME);
        fputs(rs, stdout);