archive_write_add_filter_lz4.c revision 316337
1299425Smm/*- 2299425Smm * Copyright (c) 2014 Michihiro NAKAJIMA 3299425Smm * All rights reserved. 4299425Smm * 5299425Smm * Redistribution and use in source and binary forms, with or without 6299425Smm * modification, are permitted provided that the following conditions 7299425Smm * are met: 8299425Smm * 1. Redistributions of source code must retain the above copyright 9299425Smm * notice, this list of conditions and the following disclaimer. 10299425Smm * 2. Redistributions in binary form must reproduce the above copyright 11299425Smm * notice, this list of conditions and the following disclaimer in the 12299425Smm * documentation and/or other materials provided with the distribution. 13299425Smm * 14299425Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15299425Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16299425Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17299425Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18299425Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19299425Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20299425Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21299425Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22299425Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23299425Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24299425Smm */ 25299425Smm 26299425Smm#include "archive_platform.h" 27299425Smm 28299425Smm__FBSDID("$FreeBSD: stable/11/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c 316337 2017-03-31 20:16:24Z mm $"); 29299425Smm 30299425Smm#ifdef HAVE_ERRNO_H 31299425Smm#include <errno.h> 32299425Smm#endif 33299425Smm#include <stdio.h> 34299425Smm#ifdef HAVE_STDLIB_H 35299425Smm#include <stdlib.h> 36299425Smm#endif 37299425Smm#ifdef HAVE_STRING_H 38299425Smm#include <string.h> 39299425Smm#endif 40299425Smm#ifdef HAVE_LZ4_H 41299425Smm#include <lz4.h> 42299425Smm#endif 43299425Smm#ifdef HAVE_LZ4HC_H 44299425Smm#include <lz4hc.h> 45299425Smm#endif 46299425Smm 47299425Smm#include "archive.h" 48299425Smm#include "archive_endian.h" 49299425Smm#include "archive_private.h" 50299425Smm#include "archive_write_private.h" 51299425Smm#include "archive_xxhash.h" 52299425Smm 53299425Smm#define LZ4_MAGICNUMBER 0x184d2204 54299425Smm 55299425Smmstruct private_data { 56299425Smm int compression_level; 57299425Smm unsigned header_written:1; 58299425Smm unsigned version_number:1; 59299425Smm unsigned block_independence:1; 60299425Smm unsigned block_checksum:1; 61299425Smm unsigned stream_size:1; 62299425Smm unsigned stream_checksum:1; 63299425Smm unsigned preset_dictionary:1; 64299425Smm unsigned block_maximum_size:3; 65299425Smm#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 66299425Smm int64_t total_in; 67299425Smm char *out; 68299425Smm char *out_buffer; 69299425Smm size_t out_buffer_size; 70299425Smm size_t out_block_size; 71299425Smm char *in; 72299425Smm char *in_buffer_allocated; 73299425Smm char *in_buffer; 74299425Smm size_t in_buffer_size; 75299425Smm size_t block_size; 76299425Smm 77299425Smm void *xxh32_state; 78299425Smm void *lz4_stream; 79299425Smm#else 80299425Smm struct archive_write_program_data *pdata; 81299425Smm#endif 82299425Smm}; 83299425Smm 84299425Smmstatic int archive_filter_lz4_close(struct archive_write_filter *); 85299425Smmstatic int archive_filter_lz4_free(struct archive_write_filter *); 86299425Smmstatic int archive_filter_lz4_open(struct archive_write_filter *); 87299425Smmstatic int archive_filter_lz4_options(struct archive_write_filter *, 88299425Smm const char *, const char *); 89299425Smmstatic int archive_filter_lz4_write(struct archive_write_filter *, 90299425Smm const void *, size_t); 91299425Smm 92299425Smm/* 93299425Smm * Add a lz4 compression filter to this write handle. 94299425Smm */ 95299425Smmint 96299425Smmarchive_write_add_filter_lz4(struct archive *_a) 97299425Smm{ 98299425Smm struct archive_write *a = (struct archive_write *)_a; 99299425Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 100299425Smm struct private_data *data; 101299425Smm 102299425Smm archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 103299425Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4"); 104299425Smm 105299425Smm data = calloc(1, sizeof(*data)); 106299425Smm if (data == NULL) { 107299425Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 108299425Smm return (ARCHIVE_FATAL); 109299425Smm } 110299425Smm 111299425Smm /* 112299425Smm * Setup default settings. 113299425Smm */ 114299425Smm data->compression_level = 1; 115299425Smm data->version_number = 0x01; 116299425Smm data->block_independence = 1; 117299425Smm data->block_checksum = 0; 118299425Smm data->stream_size = 0; 119299425Smm data->stream_checksum = 1; 120299425Smm data->preset_dictionary = 0; 121299425Smm data->block_maximum_size = 7; 122299425Smm 123299425Smm /* 124299425Smm * Setup a filter setting. 125299425Smm */ 126299425Smm f->data = data; 127299425Smm f->options = &archive_filter_lz4_options; 128299425Smm f->close = &archive_filter_lz4_close; 129299425Smm f->free = &archive_filter_lz4_free; 130299425Smm f->open = &archive_filter_lz4_open; 131299425Smm f->code = ARCHIVE_FILTER_LZ4; 132299425Smm f->name = "lz4"; 133299425Smm#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 134299425Smm return (ARCHIVE_OK); 135299425Smm#else 136299425Smm /* 137299425Smm * We don't have lz4 library, and execute external lz4 program 138299425Smm * instead. 139299425Smm */ 140299425Smm data->pdata = __archive_write_program_allocate("lz4"); 141299425Smm if (data->pdata == NULL) { 142299425Smm free(data); 143299425Smm archive_set_error(&a->archive, ENOMEM, "Out of memory"); 144299425Smm return (ARCHIVE_FATAL); 145299425Smm } 146299425Smm data->compression_level = 0; 147299425Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 148299425Smm "Using external lz4 program"); 149299425Smm return (ARCHIVE_WARN); 150299425Smm#endif 151299425Smm} 152299425Smm 153299425Smm/* 154299425Smm * Set write options. 155299425Smm */ 156299425Smmstatic int 157299425Smmarchive_filter_lz4_options(struct archive_write_filter *f, 158299425Smm const char *key, const char *value) 159299425Smm{ 160299425Smm struct private_data *data = (struct private_data *)f->data; 161299425Smm 162299425Smm if (strcmp(key, "compression-level") == 0) { 163299425Smm int val; 164299425Smm if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) || 165299425Smm value[1] != '\0') 166299425Smm return (ARCHIVE_WARN); 167299425Smm 168299425Smm#ifndef HAVE_LZ4HC_H 169299425Smm if(val >= 3) 170299425Smm { 171299425Smm archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, 172299425Smm "High compression not included in this build"); 173299425Smm return (ARCHIVE_FATAL); 174299425Smm } 175299425Smm#endif 176299425Smm data->compression_level = val; 177299425Smm return (ARCHIVE_OK); 178299425Smm } 179299425Smm if (strcmp(key, "stream-checksum") == 0) { 180299425Smm data->stream_checksum = value != NULL; 181299425Smm return (ARCHIVE_OK); 182299425Smm } 183299425Smm if (strcmp(key, "block-checksum") == 0) { 184299425Smm data->block_checksum = value != NULL; 185299425Smm return (ARCHIVE_OK); 186299425Smm } 187299425Smm if (strcmp(key, "block-size") == 0) { 188299425Smm if (value == NULL || !(value[0] >= '4' && value[0] <= '7') || 189299425Smm value[1] != '\0') 190299425Smm return (ARCHIVE_WARN); 191299425Smm data->block_maximum_size = value[0] - '0'; 192299425Smm return (ARCHIVE_OK); 193299425Smm } 194299425Smm if (strcmp(key, "block-dependence") == 0) { 195299425Smm data->block_independence = value == NULL; 196299425Smm return (ARCHIVE_OK); 197299425Smm } 198299425Smm 199299425Smm /* Note: The "warn" return is just to inform the options 200299425Smm * supervisor that we didn't handle it. It will generate 201299425Smm * a suitable error if no one used this option. */ 202299425Smm return (ARCHIVE_WARN); 203299425Smm} 204299425Smm 205299425Smm#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 206299425Smm/* Don't compile this if we don't have liblz4. */ 207299425Smm 208299425Smmstatic int drive_compressor(struct archive_write_filter *, const char *, 209299425Smm size_t); 210299425Smmstatic int drive_compressor_independence(struct archive_write_filter *, 211299425Smm const char *, size_t); 212299425Smmstatic int drive_compressor_dependence(struct archive_write_filter *, 213299425Smm const char *, size_t); 214299425Smmstatic int lz4_write_stream_descriptor(struct archive_write_filter *); 215299425Smmstatic ssize_t lz4_write_one_block(struct archive_write_filter *, const char *, 216299425Smm size_t); 217299425Smm 218299425Smm 219299425Smm/* 220299425Smm * Setup callback. 221299425Smm */ 222299425Smmstatic int 223299425Smmarchive_filter_lz4_open(struct archive_write_filter *f) 224299425Smm{ 225299425Smm struct private_data *data = (struct private_data *)f->data; 226299425Smm int ret; 227299425Smm size_t required_size; 228316337Smm static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 229299425Smm 4 * 1024 * 1024 }; 230299425Smm size_t pre_block_size; 231299425Smm 232299425Smm ret = __archive_write_open_filter(f->next_filter); 233299425Smm if (ret != 0) 234299425Smm return (ret); 235299425Smm 236299425Smm if (data->block_maximum_size < 4) 237299425Smm data->block_size = bkmap[0]; 238299425Smm else 239299425Smm data->block_size = bkmap[data->block_maximum_size - 4]; 240299425Smm 241299425Smm required_size = 4 + 15 + 4 + data->block_size + 4 + 4; 242299425Smm if (data->out_buffer_size < required_size) { 243299425Smm size_t bs = required_size, bpb; 244299425Smm free(data->out_buffer); 245299425Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 246299425Smm /* Buffer size should be a multiple number of 247299425Smm * the of bytes per block for performance. */ 248299425Smm bpb = archive_write_get_bytes_per_block(f->archive); 249299425Smm if (bpb > bs) 250299425Smm bs = bpb; 251299425Smm else if (bpb != 0) { 252299425Smm bs += bpb; 253299425Smm bs -= bs % bpb; 254299425Smm } 255299425Smm } 256299425Smm data->out_block_size = bs; 257299425Smm bs += required_size; 258299425Smm data->out_buffer = malloc(bs); 259299425Smm data->out = data->out_buffer; 260299425Smm data->out_buffer_size = bs; 261299425Smm } 262299425Smm 263299425Smm pre_block_size = (data->block_independence)? 0: 64 * 1024; 264299425Smm if (data->in_buffer_size < data->block_size + pre_block_size) { 265299425Smm free(data->in_buffer_allocated); 266299425Smm data->in_buffer_size = data->block_size; 267299425Smm data->in_buffer_allocated = 268299425Smm malloc(data->in_buffer_size + pre_block_size); 269299425Smm data->in_buffer = data->in_buffer_allocated + pre_block_size; 270299425Smm if (!data->block_independence && data->compression_level >= 3) 271299425Smm data->in_buffer = data->in_buffer_allocated; 272299425Smm data->in = data->in_buffer; 273299425Smm data->in_buffer_size = data->block_size; 274299425Smm } 275299425Smm 276299425Smm if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { 277299425Smm archive_set_error(f->archive, ENOMEM, 278299425Smm "Can't allocate data for compression buffer"); 279299425Smm return (ARCHIVE_FATAL); 280299425Smm } 281299425Smm 282299425Smm f->write = archive_filter_lz4_write; 283299425Smm 284299425Smm return (ARCHIVE_OK); 285299425Smm} 286299425Smm 287299425Smm/* 288299425Smm * Write data to the out stream. 289299425Smm * 290299425Smm * Returns ARCHIVE_OK if all data written, error otherwise. 291299425Smm */ 292299425Smmstatic int 293299425Smmarchive_filter_lz4_write(struct archive_write_filter *f, 294299425Smm const void *buff, size_t length) 295299425Smm{ 296299425Smm struct private_data *data = (struct private_data *)f->data; 297299425Smm int ret = ARCHIVE_OK; 298299425Smm const char *p; 299299425Smm size_t remaining; 300299425Smm ssize_t size; 301299425Smm 302299425Smm /* If we haven't written a stream descriptor, we have to do it first. */ 303299425Smm if (!data->header_written) { 304299425Smm ret = lz4_write_stream_descriptor(f); 305299425Smm if (ret != ARCHIVE_OK) 306299425Smm return (ret); 307299425Smm data->header_written = 1; 308299425Smm } 309299425Smm 310299425Smm /* Update statistics */ 311299425Smm data->total_in += length; 312299425Smm 313299425Smm p = (const char *)buff; 314299425Smm remaining = length; 315299425Smm while (remaining) { 316299425Smm size_t l; 317299425Smm /* Compress input data to output buffer */ 318299425Smm size = lz4_write_one_block(f, p, remaining); 319299425Smm if (size < ARCHIVE_OK) 320299425Smm return (ARCHIVE_FATAL); 321299425Smm l = data->out - data->out_buffer; 322299425Smm if (l >= data->out_block_size) { 323299425Smm ret = __archive_write_filter(f->next_filter, 324299425Smm data->out_buffer, data->out_block_size); 325299425Smm l -= data->out_block_size; 326299425Smm memcpy(data->out_buffer, 327299425Smm data->out_buffer + data->out_block_size, l); 328299425Smm data->out = data->out_buffer + l; 329299425Smm if (ret < ARCHIVE_WARN) 330299425Smm break; 331299425Smm } 332299425Smm p += size; 333299425Smm remaining -= size; 334299425Smm } 335299425Smm 336299425Smm return (ret); 337299425Smm} 338299425Smm 339299425Smm/* 340299425Smm * Finish the compression. 341299425Smm */ 342299425Smmstatic int 343299425Smmarchive_filter_lz4_close(struct archive_write_filter *f) 344299425Smm{ 345299425Smm struct private_data *data = (struct private_data *)f->data; 346299425Smm int ret, r1; 347299425Smm 348299425Smm /* Finish compression cycle. */ 349299425Smm ret = (int)lz4_write_one_block(f, NULL, 0); 350299425Smm if (ret >= 0) { 351299425Smm /* 352299425Smm * Write the last block and the end of the stream data. 353299425Smm */ 354299425Smm 355299425Smm /* Write End Of Stream. */ 356299425Smm memset(data->out, 0, 4); data->out += 4; 357299425Smm /* Write Stream checksum if needed. */ 358299425Smm if (data->stream_checksum) { 359299425Smm unsigned int checksum; 360299425Smm checksum = __archive_xxhash.XXH32_digest( 361299425Smm data->xxh32_state); 362299425Smm data->xxh32_state = NULL; 363299425Smm archive_le32enc(data->out, checksum); 364299425Smm data->out += 4; 365299425Smm } 366299425Smm ret = __archive_write_filter(f->next_filter, 367299425Smm data->out_buffer, data->out - data->out_buffer); 368299425Smm } 369299425Smm 370299425Smm r1 = __archive_write_close_filter(f->next_filter); 371299425Smm return (r1 < ret ? r1 : ret); 372299425Smm} 373299425Smm 374299425Smmstatic int 375299425Smmarchive_filter_lz4_free(struct archive_write_filter *f) 376299425Smm{ 377299425Smm struct private_data *data = (struct private_data *)f->data; 378299425Smm 379299425Smm if (data->lz4_stream != NULL) { 380299425Smm#ifdef HAVE_LZ4HC_H 381299425Smm if (data->compression_level >= 3) 382299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 383299425Smm LZ4_freeStreamHC(data->lz4_stream); 384299425Smm#else 385299425Smm LZ4_freeHC(data->lz4_stream); 386299425Smm#endif 387299425Smm else 388299425Smm#endif 389299425Smm#if LZ4_VERSION_MINOR >= 3 390299425Smm LZ4_freeStream(data->lz4_stream); 391299425Smm#else 392299425Smm LZ4_free(data->lz4_stream); 393299425Smm#endif 394299425Smm } 395299425Smm free(data->out_buffer); 396299425Smm free(data->in_buffer_allocated); 397299425Smm free(data->xxh32_state); 398299425Smm free(data); 399299425Smm f->data = NULL; 400299425Smm return (ARCHIVE_OK); 401299425Smm} 402299425Smm 403299425Smmstatic int 404299425Smmlz4_write_stream_descriptor(struct archive_write_filter *f) 405299425Smm{ 406299425Smm struct private_data *data = (struct private_data *)f->data; 407299425Smm uint8_t *sd; 408299425Smm 409299425Smm sd = (uint8_t *)data->out; 410299425Smm /* Write Magic Number. */ 411299425Smm archive_le32enc(&sd[0], LZ4_MAGICNUMBER); 412299425Smm /* FLG */ 413299425Smm sd[4] = (data->version_number << 6) 414299425Smm | (data->block_independence << 5) 415299425Smm | (data->block_checksum << 4) 416299425Smm | (data->stream_size << 3) 417299425Smm | (data->stream_checksum << 2) 418299425Smm | (data->preset_dictionary << 0); 419299425Smm /* BD */ 420299425Smm sd[5] = (data->block_maximum_size << 4); 421299425Smm sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; 422299425Smm data->out += 7; 423299425Smm if (data->stream_checksum) 424299425Smm data->xxh32_state = __archive_xxhash.XXH32_init(0); 425299425Smm else 426299425Smm data->xxh32_state = NULL; 427299425Smm return (ARCHIVE_OK); 428299425Smm} 429299425Smm 430299425Smmstatic ssize_t 431299425Smmlz4_write_one_block(struct archive_write_filter *f, const char *p, 432299425Smm size_t length) 433299425Smm{ 434299425Smm struct private_data *data = (struct private_data *)f->data; 435299425Smm ssize_t r; 436299425Smm 437299425Smm if (p == NULL) { 438299425Smm /* Compress remaining uncompressed data. */ 439299425Smm if (data->in_buffer == data->in) 440299425Smm return 0; 441299425Smm else { 442299425Smm size_t l = data->in - data->in_buffer; 443299425Smm r = drive_compressor(f, data->in_buffer, l); 444299425Smm if (r == ARCHIVE_OK) 445299425Smm r = (ssize_t)l; 446299425Smm } 447299425Smm } else if ((data->block_independence || data->compression_level < 3) && 448299425Smm data->in_buffer == data->in && length >= data->block_size) { 449299425Smm r = drive_compressor(f, p, data->block_size); 450299425Smm if (r == ARCHIVE_OK) 451299425Smm r = (ssize_t)data->block_size; 452299425Smm } else { 453299425Smm size_t remaining_size = data->in_buffer_size - 454299425Smm (data->in - data->in_buffer); 455299425Smm size_t l = (remaining_size > length)? length: remaining_size; 456299425Smm memcpy(data->in, p, l); 457299425Smm data->in += l; 458299425Smm if (l == remaining_size) { 459299425Smm r = drive_compressor(f, data->in_buffer, 460299425Smm data->block_size); 461299425Smm if (r == ARCHIVE_OK) 462299425Smm r = (ssize_t)l; 463299425Smm data->in = data->in_buffer; 464299425Smm } else 465299425Smm r = (ssize_t)l; 466299425Smm } 467299425Smm 468299425Smm return (r); 469299425Smm} 470299425Smm 471299425Smm 472299425Smm/* 473299425Smm * Utility function to push input data through compressor, writing 474299425Smm * full output blocks as necessary. 475299425Smm * 476299425Smm * Note that this handles both the regular write case (finishing == 477299425Smm * false) and the end-of-archive case (finishing == true). 478299425Smm */ 479299425Smmstatic int 480299425Smmdrive_compressor(struct archive_write_filter *f, const char *p, size_t length) 481299425Smm{ 482299425Smm struct private_data *data = (struct private_data *)f->data; 483299425Smm 484299425Smm if (data->stream_checksum) 485299425Smm __archive_xxhash.XXH32_update(data->xxh32_state, 486299425Smm p, (int)length); 487299425Smm if (data->block_independence) 488299425Smm return drive_compressor_independence(f, p, length); 489299425Smm else 490299425Smm return drive_compressor_dependence(f, p, length); 491299425Smm} 492299425Smm 493299425Smmstatic int 494299425Smmdrive_compressor_independence(struct archive_write_filter *f, const char *p, 495299425Smm size_t length) 496299425Smm{ 497299425Smm struct private_data *data = (struct private_data *)f->data; 498299425Smm unsigned int outsize; 499299425Smm 500299425Smm#ifdef HAVE_LZ4HC_H 501299425Smm if (data->compression_level >= 3) 502299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 503299425Smm outsize = LZ4_compress_HC(p, data->out + 4, 504299425Smm (int)length, (int)data->block_size, 505299425Smm data->compression_level); 506299425Smm#else 507299425Smm outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, 508299425Smm (int)length, (int)data->block_size, 509299425Smm data->compression_level); 510299425Smm#endif 511299425Smm else 512299425Smm#endif 513299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 514299425Smm outsize = LZ4_compress_default(p, data->out + 4, 515299425Smm (int)length, (int)data->block_size); 516299425Smm#else 517299425Smm outsize = LZ4_compress_limitedOutput(p, data->out + 4, 518299425Smm (int)length, (int)data->block_size); 519299425Smm#endif 520299425Smm 521299425Smm if (outsize) { 522299425Smm /* The buffer is compressed. */ 523299425Smm archive_le32enc(data->out, outsize); 524299425Smm data->out += 4; 525299425Smm } else { 526311041Smm /* The buffer is not compressed. The compressed size was 527299425Smm * bigger than its uncompressed size. */ 528299425Smm archive_le32enc(data->out, length | 0x80000000); 529299425Smm data->out += 4; 530299425Smm memcpy(data->out, p, length); 531299425Smm outsize = length; 532299425Smm } 533299425Smm data->out += outsize; 534299425Smm if (data->block_checksum) { 535299425Smm unsigned int checksum = 536299425Smm __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 537299425Smm archive_le32enc(data->out, checksum); 538299425Smm data->out += 4; 539299425Smm } 540299425Smm return (ARCHIVE_OK); 541299425Smm} 542299425Smm 543299425Smmstatic int 544299425Smmdrive_compressor_dependence(struct archive_write_filter *f, const char *p, 545299425Smm size_t length) 546299425Smm{ 547299425Smm struct private_data *data = (struct private_data *)f->data; 548299425Smm int outsize; 549299425Smm 550299425Smm#define DICT_SIZE (64 * 1024) 551299425Smm#ifdef HAVE_LZ4HC_H 552299425Smm if (data->compression_level >= 3) { 553299425Smm if (data->lz4_stream == NULL) { 554299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 555299425Smm data->lz4_stream = LZ4_createStreamHC(); 556299425Smm LZ4_resetStreamHC(data->lz4_stream, data->compression_level); 557299425Smm#else 558299425Smm data->lz4_stream = 559299425Smm LZ4_createHC(data->in_buffer_allocated); 560299425Smm#endif 561299425Smm if (data->lz4_stream == NULL) { 562299425Smm archive_set_error(f->archive, ENOMEM, 563299425Smm "Can't allocate data for compression" 564299425Smm " buffer"); 565299425Smm return (ARCHIVE_FATAL); 566299425Smm } 567299425Smm } 568299425Smm else 569299425Smm LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 570299425Smm 571299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 572299425Smm outsize = LZ4_compress_HC_continue( 573299425Smm data->lz4_stream, p, data->out + 4, (int)length, 574299425Smm (int)data->block_size); 575299425Smm#else 576299425Smm outsize = LZ4_compressHC2_limitedOutput_continue( 577299425Smm data->lz4_stream, p, data->out + 4, (int)length, 578299425Smm (int)data->block_size, data->compression_level); 579299425Smm#endif 580299425Smm } else 581299425Smm#endif 582299425Smm { 583299425Smm if (data->lz4_stream == NULL) { 584299425Smm data->lz4_stream = LZ4_createStream(); 585299425Smm if (data->lz4_stream == NULL) { 586299425Smm archive_set_error(f->archive, ENOMEM, 587299425Smm "Can't allocate data for compression" 588299425Smm " buffer"); 589299425Smm return (ARCHIVE_FATAL); 590299425Smm } 591299425Smm } 592299425Smm else 593299425Smm LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 594299425Smm 595299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 596299425Smm outsize = LZ4_compress_fast_continue( 597299425Smm data->lz4_stream, p, data->out + 4, (int)length, 598299425Smm (int)data->block_size, 1); 599299425Smm#else 600299425Smm outsize = LZ4_compress_limitedOutput_continue( 601299425Smm data->lz4_stream, p, data->out + 4, (int)length, 602299425Smm (int)data->block_size); 603299425Smm#endif 604299425Smm } 605299425Smm 606299425Smm if (outsize) { 607299425Smm /* The buffer is compressed. */ 608299425Smm archive_le32enc(data->out, outsize); 609299425Smm data->out += 4; 610299425Smm } else { 611311041Smm /* The buffer is not compressed. The compressed size was 612299425Smm * bigger than its uncompressed size. */ 613299425Smm archive_le32enc(data->out, length | 0x80000000); 614299425Smm data->out += 4; 615299425Smm memcpy(data->out, p, length); 616299425Smm outsize = length; 617299425Smm } 618299425Smm data->out += outsize; 619299425Smm if (data->block_checksum) { 620299425Smm unsigned int checksum = 621299425Smm __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 622299425Smm archive_le32enc(data->out, checksum); 623299425Smm data->out += 4; 624299425Smm } 625299425Smm 626299425Smm if (length == data->block_size) { 627299425Smm#ifdef HAVE_LZ4HC_H 628299425Smm if (data->compression_level >= 3) { 629299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 630299425Smm LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 631299425Smm#else 632299425Smm LZ4_slideInputBufferHC(data->lz4_stream); 633299425Smm#endif 634299425Smm data->in_buffer = data->in_buffer_allocated + DICT_SIZE; 635299425Smm } 636299425Smm else 637299425Smm#endif 638299425Smm LZ4_saveDict(data->lz4_stream, 639299425Smm data->in_buffer_allocated, DICT_SIZE); 640299425Smm#undef DICT_SIZE 641299425Smm } 642299425Smm return (ARCHIVE_OK); 643299425Smm} 644299425Smm 645299425Smm#else /* HAVE_LIBLZ4 */ 646299425Smm 647299425Smmstatic int 648299425Smmarchive_filter_lz4_open(struct archive_write_filter *f) 649299425Smm{ 650299425Smm struct private_data *data = (struct private_data *)f->data; 651299425Smm struct archive_string as; 652299425Smm int r; 653299425Smm 654299425Smm archive_string_init(&as); 655299425Smm archive_strcpy(&as, "lz4 -z -q -q"); 656299425Smm 657299425Smm /* Specify a compression level. */ 658299425Smm if (data->compression_level > 0) { 659299425Smm archive_strcat(&as, " -"); 660299425Smm archive_strappend_char(&as, '0' + data->compression_level); 661299425Smm } 662299425Smm /* Specify a block size. */ 663299425Smm archive_strcat(&as, " -B"); 664299425Smm archive_strappend_char(&as, '0' + data->block_maximum_size); 665299425Smm 666299425Smm if (data->block_checksum) 667299425Smm archive_strcat(&as, " -BX"); 668299425Smm if (data->stream_checksum == 0) 669299425Smm archive_strcat(&as, " --no-frame-crc"); 670299425Smm if (data->block_independence == 0) 671299425Smm archive_strcat(&as, " -BD"); 672299425Smm 673299425Smm f->write = archive_filter_lz4_write; 674299425Smm 675299425Smm r = __archive_write_program_open(f, data->pdata, as.s); 676299425Smm archive_string_free(&as); 677299425Smm return (r); 678299425Smm} 679299425Smm 680299425Smmstatic int 681299425Smmarchive_filter_lz4_write(struct archive_write_filter *f, const void *buff, 682299425Smm size_t length) 683299425Smm{ 684299425Smm struct private_data *data = (struct private_data *)f->data; 685299425Smm 686299425Smm return __archive_write_program_write(f, data->pdata, buff, length); 687299425Smm} 688299425Smm 689299425Smmstatic int 690299425Smmarchive_filter_lz4_close(struct archive_write_filter *f) 691299425Smm{ 692299425Smm struct private_data *data = (struct private_data *)f->data; 693299425Smm 694299425Smm return __archive_write_program_close(f, data->pdata); 695299425Smm} 696299425Smm 697299425Smmstatic int 698299425Smmarchive_filter_lz4_free(struct archive_write_filter *f) 699299425Smm{ 700299425Smm struct private_data *data = (struct private_data *)f->data; 701299425Smm 702299425Smm __archive_write_program_free(data->pdata); 703299425Smm free(data); 704299425Smm return (ARCHIVE_OK); 705299425Smm} 706299425Smm 707299425Smm#endif /* HAVE_LIBLZ4 */ 708