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 358088 2020-02-19 01:50:47Z 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 size_t required_size; 227316337Smm static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 228299425Smm 4 * 1024 * 1024 }; 229299425Smm size_t pre_block_size; 230299425Smm 231299425Smm if (data->block_maximum_size < 4) 232299425Smm data->block_size = bkmap[0]; 233299425Smm else 234299425Smm data->block_size = bkmap[data->block_maximum_size - 4]; 235299425Smm 236299425Smm required_size = 4 + 15 + 4 + data->block_size + 4 + 4; 237299425Smm if (data->out_buffer_size < required_size) { 238299425Smm size_t bs = required_size, bpb; 239299425Smm free(data->out_buffer); 240299425Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 241299425Smm /* Buffer size should be a multiple number of 242299425Smm * the of bytes per block for performance. */ 243299425Smm bpb = archive_write_get_bytes_per_block(f->archive); 244299425Smm if (bpb > bs) 245299425Smm bs = bpb; 246299425Smm else if (bpb != 0) { 247299425Smm bs += bpb; 248299425Smm bs -= bs % bpb; 249299425Smm } 250299425Smm } 251299425Smm data->out_block_size = bs; 252299425Smm bs += required_size; 253299425Smm data->out_buffer = malloc(bs); 254299425Smm data->out = data->out_buffer; 255299425Smm data->out_buffer_size = bs; 256299425Smm } 257299425Smm 258299425Smm pre_block_size = (data->block_independence)? 0: 64 * 1024; 259299425Smm if (data->in_buffer_size < data->block_size + pre_block_size) { 260299425Smm free(data->in_buffer_allocated); 261299425Smm data->in_buffer_size = data->block_size; 262299425Smm data->in_buffer_allocated = 263299425Smm malloc(data->in_buffer_size + pre_block_size); 264299425Smm data->in_buffer = data->in_buffer_allocated + pre_block_size; 265299425Smm if (!data->block_independence && data->compression_level >= 3) 266299425Smm data->in_buffer = data->in_buffer_allocated; 267299425Smm data->in = data->in_buffer; 268299425Smm data->in_buffer_size = data->block_size; 269299425Smm } 270299425Smm 271299425Smm if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { 272299425Smm archive_set_error(f->archive, ENOMEM, 273299425Smm "Can't allocate data for compression buffer"); 274299425Smm return (ARCHIVE_FATAL); 275299425Smm } 276299425Smm 277299425Smm f->write = archive_filter_lz4_write; 278299425Smm 279299425Smm return (ARCHIVE_OK); 280299425Smm} 281299425Smm 282299425Smm/* 283299425Smm * Write data to the out stream. 284299425Smm * 285299425Smm * Returns ARCHIVE_OK if all data written, error otherwise. 286299425Smm */ 287299425Smmstatic int 288299425Smmarchive_filter_lz4_write(struct archive_write_filter *f, 289299425Smm const void *buff, size_t length) 290299425Smm{ 291299425Smm struct private_data *data = (struct private_data *)f->data; 292299425Smm int ret = ARCHIVE_OK; 293299425Smm const char *p; 294299425Smm size_t remaining; 295299425Smm ssize_t size; 296299425Smm 297299425Smm /* If we haven't written a stream descriptor, we have to do it first. */ 298299425Smm if (!data->header_written) { 299299425Smm ret = lz4_write_stream_descriptor(f); 300299425Smm if (ret != ARCHIVE_OK) 301299425Smm return (ret); 302299425Smm data->header_written = 1; 303299425Smm } 304299425Smm 305299425Smm /* Update statistics */ 306299425Smm data->total_in += length; 307299425Smm 308299425Smm p = (const char *)buff; 309299425Smm remaining = length; 310299425Smm while (remaining) { 311299425Smm size_t l; 312299425Smm /* Compress input data to output buffer */ 313299425Smm size = lz4_write_one_block(f, p, remaining); 314299425Smm if (size < ARCHIVE_OK) 315299425Smm return (ARCHIVE_FATAL); 316299425Smm l = data->out - data->out_buffer; 317299425Smm if (l >= data->out_block_size) { 318299425Smm ret = __archive_write_filter(f->next_filter, 319299425Smm data->out_buffer, data->out_block_size); 320299425Smm l -= data->out_block_size; 321299425Smm memcpy(data->out_buffer, 322299425Smm data->out_buffer + data->out_block_size, l); 323299425Smm data->out = data->out_buffer + l; 324299425Smm if (ret < ARCHIVE_WARN) 325299425Smm break; 326299425Smm } 327299425Smm p += size; 328299425Smm remaining -= size; 329299425Smm } 330299425Smm 331299425Smm return (ret); 332299425Smm} 333299425Smm 334299425Smm/* 335299425Smm * Finish the compression. 336299425Smm */ 337299425Smmstatic int 338299425Smmarchive_filter_lz4_close(struct archive_write_filter *f) 339299425Smm{ 340299425Smm struct private_data *data = (struct private_data *)f->data; 341358088Smm int ret; 342299425Smm 343299425Smm /* Finish compression cycle. */ 344299425Smm ret = (int)lz4_write_one_block(f, NULL, 0); 345299425Smm if (ret >= 0) { 346299425Smm /* 347299425Smm * Write the last block and the end of the stream data. 348299425Smm */ 349299425Smm 350299425Smm /* Write End Of Stream. */ 351299425Smm memset(data->out, 0, 4); data->out += 4; 352299425Smm /* Write Stream checksum if needed. */ 353299425Smm if (data->stream_checksum) { 354299425Smm unsigned int checksum; 355299425Smm checksum = __archive_xxhash.XXH32_digest( 356299425Smm data->xxh32_state); 357299425Smm data->xxh32_state = NULL; 358299425Smm archive_le32enc(data->out, checksum); 359299425Smm data->out += 4; 360299425Smm } 361299425Smm ret = __archive_write_filter(f->next_filter, 362299425Smm data->out_buffer, data->out - data->out_buffer); 363299425Smm } 364358088Smm return ret; 365299425Smm} 366299425Smm 367299425Smmstatic int 368299425Smmarchive_filter_lz4_free(struct archive_write_filter *f) 369299425Smm{ 370299425Smm struct private_data *data = (struct private_data *)f->data; 371299425Smm 372299425Smm if (data->lz4_stream != NULL) { 373299425Smm#ifdef HAVE_LZ4HC_H 374299425Smm if (data->compression_level >= 3) 375299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 376299425Smm LZ4_freeStreamHC(data->lz4_stream); 377299425Smm#else 378299425Smm LZ4_freeHC(data->lz4_stream); 379299425Smm#endif 380299425Smm else 381299425Smm#endif 382299425Smm#if LZ4_VERSION_MINOR >= 3 383299425Smm LZ4_freeStream(data->lz4_stream); 384299425Smm#else 385299425Smm LZ4_free(data->lz4_stream); 386299425Smm#endif 387299425Smm } 388299425Smm free(data->out_buffer); 389299425Smm free(data->in_buffer_allocated); 390299425Smm free(data->xxh32_state); 391299425Smm free(data); 392299425Smm f->data = NULL; 393299425Smm return (ARCHIVE_OK); 394299425Smm} 395299425Smm 396299425Smmstatic int 397299425Smmlz4_write_stream_descriptor(struct archive_write_filter *f) 398299425Smm{ 399299425Smm struct private_data *data = (struct private_data *)f->data; 400299425Smm uint8_t *sd; 401299425Smm 402299425Smm sd = (uint8_t *)data->out; 403299425Smm /* Write Magic Number. */ 404299425Smm archive_le32enc(&sd[0], LZ4_MAGICNUMBER); 405299425Smm /* FLG */ 406299425Smm sd[4] = (data->version_number << 6) 407299425Smm | (data->block_independence << 5) 408299425Smm | (data->block_checksum << 4) 409299425Smm | (data->stream_size << 3) 410299425Smm | (data->stream_checksum << 2) 411299425Smm | (data->preset_dictionary << 0); 412299425Smm /* BD */ 413299425Smm sd[5] = (data->block_maximum_size << 4); 414299425Smm sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; 415299425Smm data->out += 7; 416299425Smm if (data->stream_checksum) 417299425Smm data->xxh32_state = __archive_xxhash.XXH32_init(0); 418299425Smm else 419299425Smm data->xxh32_state = NULL; 420299425Smm return (ARCHIVE_OK); 421299425Smm} 422299425Smm 423299425Smmstatic ssize_t 424299425Smmlz4_write_one_block(struct archive_write_filter *f, const char *p, 425299425Smm size_t length) 426299425Smm{ 427299425Smm struct private_data *data = (struct private_data *)f->data; 428299425Smm ssize_t r; 429299425Smm 430299425Smm if (p == NULL) { 431299425Smm /* Compress remaining uncompressed data. */ 432299425Smm if (data->in_buffer == data->in) 433299425Smm return 0; 434299425Smm else { 435299425Smm size_t l = data->in - data->in_buffer; 436299425Smm r = drive_compressor(f, data->in_buffer, l); 437299425Smm if (r == ARCHIVE_OK) 438299425Smm r = (ssize_t)l; 439299425Smm } 440299425Smm } else if ((data->block_independence || data->compression_level < 3) && 441299425Smm data->in_buffer == data->in && length >= data->block_size) { 442299425Smm r = drive_compressor(f, p, data->block_size); 443299425Smm if (r == ARCHIVE_OK) 444299425Smm r = (ssize_t)data->block_size; 445299425Smm } else { 446299425Smm size_t remaining_size = data->in_buffer_size - 447299425Smm (data->in - data->in_buffer); 448299425Smm size_t l = (remaining_size > length)? length: remaining_size; 449299425Smm memcpy(data->in, p, l); 450299425Smm data->in += l; 451299425Smm if (l == remaining_size) { 452299425Smm r = drive_compressor(f, data->in_buffer, 453299425Smm data->block_size); 454299425Smm if (r == ARCHIVE_OK) 455299425Smm r = (ssize_t)l; 456299425Smm data->in = data->in_buffer; 457299425Smm } else 458299425Smm r = (ssize_t)l; 459299425Smm } 460299425Smm 461299425Smm return (r); 462299425Smm} 463299425Smm 464299425Smm 465299425Smm/* 466299425Smm * Utility function to push input data through compressor, writing 467299425Smm * full output blocks as necessary. 468299425Smm * 469299425Smm * Note that this handles both the regular write case (finishing == 470299425Smm * false) and the end-of-archive case (finishing == true). 471299425Smm */ 472299425Smmstatic int 473299425Smmdrive_compressor(struct archive_write_filter *f, const char *p, size_t length) 474299425Smm{ 475299425Smm struct private_data *data = (struct private_data *)f->data; 476299425Smm 477299425Smm if (data->stream_checksum) 478299425Smm __archive_xxhash.XXH32_update(data->xxh32_state, 479299425Smm p, (int)length); 480299425Smm if (data->block_independence) 481299425Smm return drive_compressor_independence(f, p, length); 482299425Smm else 483299425Smm return drive_compressor_dependence(f, p, length); 484299425Smm} 485299425Smm 486299425Smmstatic int 487299425Smmdrive_compressor_independence(struct archive_write_filter *f, const char *p, 488299425Smm size_t length) 489299425Smm{ 490299425Smm struct private_data *data = (struct private_data *)f->data; 491299425Smm unsigned int outsize; 492299425Smm 493299425Smm#ifdef HAVE_LZ4HC_H 494299425Smm if (data->compression_level >= 3) 495299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 496299425Smm outsize = LZ4_compress_HC(p, data->out + 4, 497299425Smm (int)length, (int)data->block_size, 498299425Smm data->compression_level); 499299425Smm#else 500299425Smm outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, 501299425Smm (int)length, (int)data->block_size, 502299425Smm data->compression_level); 503299425Smm#endif 504299425Smm else 505299425Smm#endif 506299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 507299425Smm outsize = LZ4_compress_default(p, data->out + 4, 508299425Smm (int)length, (int)data->block_size); 509299425Smm#else 510299425Smm outsize = LZ4_compress_limitedOutput(p, data->out + 4, 511299425Smm (int)length, (int)data->block_size); 512299425Smm#endif 513299425Smm 514299425Smm if (outsize) { 515299425Smm /* The buffer is compressed. */ 516299425Smm archive_le32enc(data->out, outsize); 517299425Smm data->out += 4; 518299425Smm } else { 519311041Smm /* The buffer is not compressed. The compressed size was 520299425Smm * bigger than its uncompressed size. */ 521299425Smm archive_le32enc(data->out, length | 0x80000000); 522299425Smm data->out += 4; 523299425Smm memcpy(data->out, p, length); 524299425Smm outsize = length; 525299425Smm } 526299425Smm data->out += outsize; 527299425Smm if (data->block_checksum) { 528299425Smm unsigned int checksum = 529299425Smm __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 530299425Smm archive_le32enc(data->out, checksum); 531299425Smm data->out += 4; 532299425Smm } 533299425Smm return (ARCHIVE_OK); 534299425Smm} 535299425Smm 536299425Smmstatic int 537299425Smmdrive_compressor_dependence(struct archive_write_filter *f, const char *p, 538299425Smm size_t length) 539299425Smm{ 540299425Smm struct private_data *data = (struct private_data *)f->data; 541299425Smm int outsize; 542299425Smm 543299425Smm#define DICT_SIZE (64 * 1024) 544299425Smm#ifdef HAVE_LZ4HC_H 545299425Smm if (data->compression_level >= 3) { 546299425Smm if (data->lz4_stream == NULL) { 547299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 548299425Smm data->lz4_stream = LZ4_createStreamHC(); 549299425Smm LZ4_resetStreamHC(data->lz4_stream, data->compression_level); 550299425Smm#else 551299425Smm data->lz4_stream = 552299425Smm LZ4_createHC(data->in_buffer_allocated); 553299425Smm#endif 554299425Smm if (data->lz4_stream == NULL) { 555299425Smm archive_set_error(f->archive, ENOMEM, 556299425Smm "Can't allocate data for compression" 557299425Smm " buffer"); 558299425Smm return (ARCHIVE_FATAL); 559299425Smm } 560299425Smm } 561299425Smm else 562299425Smm LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 563299425Smm 564299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 565299425Smm outsize = LZ4_compress_HC_continue( 566299425Smm data->lz4_stream, p, data->out + 4, (int)length, 567299425Smm (int)data->block_size); 568299425Smm#else 569299425Smm outsize = LZ4_compressHC2_limitedOutput_continue( 570299425Smm data->lz4_stream, p, data->out + 4, (int)length, 571299425Smm (int)data->block_size, data->compression_level); 572299425Smm#endif 573299425Smm } else 574299425Smm#endif 575299425Smm { 576299425Smm if (data->lz4_stream == NULL) { 577299425Smm data->lz4_stream = LZ4_createStream(); 578299425Smm if (data->lz4_stream == NULL) { 579299425Smm archive_set_error(f->archive, ENOMEM, 580299425Smm "Can't allocate data for compression" 581299425Smm " buffer"); 582299425Smm return (ARCHIVE_FATAL); 583299425Smm } 584299425Smm } 585299425Smm else 586299425Smm LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 587299425Smm 588299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 589299425Smm outsize = LZ4_compress_fast_continue( 590299425Smm data->lz4_stream, p, data->out + 4, (int)length, 591299425Smm (int)data->block_size, 1); 592299425Smm#else 593299425Smm outsize = LZ4_compress_limitedOutput_continue( 594299425Smm data->lz4_stream, p, data->out + 4, (int)length, 595299425Smm (int)data->block_size); 596299425Smm#endif 597299425Smm } 598299425Smm 599299425Smm if (outsize) { 600299425Smm /* The buffer is compressed. */ 601299425Smm archive_le32enc(data->out, outsize); 602299425Smm data->out += 4; 603299425Smm } else { 604311041Smm /* The buffer is not compressed. The compressed size was 605299425Smm * bigger than its uncompressed size. */ 606299425Smm archive_le32enc(data->out, length | 0x80000000); 607299425Smm data->out += 4; 608299425Smm memcpy(data->out, p, length); 609299425Smm outsize = length; 610299425Smm } 611299425Smm data->out += outsize; 612299425Smm if (data->block_checksum) { 613299425Smm unsigned int checksum = 614299425Smm __archive_xxhash.XXH32(data->out - outsize, outsize, 0); 615299425Smm archive_le32enc(data->out, checksum); 616299425Smm data->out += 4; 617299425Smm } 618299425Smm 619299425Smm if (length == data->block_size) { 620299425Smm#ifdef HAVE_LZ4HC_H 621299425Smm if (data->compression_level >= 3) { 622299425Smm#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 623299425Smm LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); 624299425Smm#else 625299425Smm LZ4_slideInputBufferHC(data->lz4_stream); 626299425Smm#endif 627299425Smm data->in_buffer = data->in_buffer_allocated + DICT_SIZE; 628299425Smm } 629299425Smm else 630299425Smm#endif 631299425Smm LZ4_saveDict(data->lz4_stream, 632299425Smm data->in_buffer_allocated, DICT_SIZE); 633299425Smm#undef DICT_SIZE 634299425Smm } 635299425Smm return (ARCHIVE_OK); 636299425Smm} 637299425Smm 638299425Smm#else /* HAVE_LIBLZ4 */ 639299425Smm 640299425Smmstatic int 641299425Smmarchive_filter_lz4_open(struct archive_write_filter *f) 642299425Smm{ 643299425Smm struct private_data *data = (struct private_data *)f->data; 644299425Smm struct archive_string as; 645299425Smm int r; 646299425Smm 647299425Smm archive_string_init(&as); 648299425Smm archive_strcpy(&as, "lz4 -z -q -q"); 649299425Smm 650299425Smm /* Specify a compression level. */ 651299425Smm if (data->compression_level > 0) { 652299425Smm archive_strcat(&as, " -"); 653299425Smm archive_strappend_char(&as, '0' + data->compression_level); 654299425Smm } 655299425Smm /* Specify a block size. */ 656299425Smm archive_strcat(&as, " -B"); 657299425Smm archive_strappend_char(&as, '0' + data->block_maximum_size); 658299425Smm 659299425Smm if (data->block_checksum) 660299425Smm archive_strcat(&as, " -BX"); 661299425Smm if (data->stream_checksum == 0) 662299425Smm archive_strcat(&as, " --no-frame-crc"); 663299425Smm if (data->block_independence == 0) 664299425Smm archive_strcat(&as, " -BD"); 665299425Smm 666299425Smm f->write = archive_filter_lz4_write; 667299425Smm 668299425Smm r = __archive_write_program_open(f, data->pdata, as.s); 669299425Smm archive_string_free(&as); 670299425Smm return (r); 671299425Smm} 672299425Smm 673299425Smmstatic int 674299425Smmarchive_filter_lz4_write(struct archive_write_filter *f, const void *buff, 675299425Smm size_t length) 676299425Smm{ 677299425Smm struct private_data *data = (struct private_data *)f->data; 678299425Smm 679299425Smm return __archive_write_program_write(f, data->pdata, buff, length); 680299425Smm} 681299425Smm 682299425Smmstatic int 683299425Smmarchive_filter_lz4_close(struct archive_write_filter *f) 684299425Smm{ 685299425Smm struct private_data *data = (struct private_data *)f->data; 686299425Smm 687299425Smm return __archive_write_program_close(f, data->pdata); 688299425Smm} 689299425Smm 690299425Smmstatic int 691299425Smmarchive_filter_lz4_free(struct archive_write_filter *f) 692299425Smm{ 693299425Smm struct private_data *data = (struct private_data *)f->data; 694299425Smm 695299425Smm __archive_write_program_free(data->pdata); 696299425Smm free(data); 697299425Smm return (ARCHIVE_OK); 698299425Smm} 699299425Smm 700299425Smm#endif /* HAVE_LIBLZ4 */ 701