xref: /freebsd/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c (revision eb5165bb491138f60d9004bc4c781490016d9288)
1 /*-
2  * Copyright (c) 2014 Michihiro NAKAJIMA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "archive_platform.h"
27 
28 #ifdef HAVE_ERRNO_H
29 #include <errno.h>
30 #endif
31 #include <stdio.h>
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 #ifdef HAVE_LZ4_H
39 #include <lz4.h>
40 #endif
41 #ifdef HAVE_LZ4HC_H
42 #include <lz4hc.h>
43 #endif
44 
45 #include "archive.h"
46 #include "archive_endian.h"
47 #include "archive_private.h"
48 #include "archive_write_private.h"
49 #include "archive_xxhash.h"
50 
51 #define LZ4_MAGICNUMBER	0x184d2204
52 
53 struct private_data {
54 	int		 compression_level;
55 	unsigned	 header_written:1;
56 	unsigned	 version_number:1;
57 	unsigned	 block_independence:1;
58 	unsigned	 block_checksum:1;
59 	unsigned	 stream_size:1;
60 	unsigned	 stream_checksum:1;
61 	unsigned	 preset_dictionary:1;
62 	unsigned	 block_maximum_size:3;
63 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
64 	int64_t		 total_in;
65 	char		*out;
66 	char		*out_buffer;
67 	size_t		 out_buffer_size;
68 	size_t		 out_block_size;
69 	char		*in;
70 	char		*in_buffer_allocated;
71 	char		*in_buffer;
72 	size_t		 in_buffer_size;
73 	size_t		 block_size;
74 
75 	void		*xxh32_state;
76 	void		*lz4_stream;
77 #else
78 	struct archive_write_program_data *pdata;
79 #endif
80 };
81 
82 static int archive_filter_lz4_close(struct archive_write_filter *);
83 static int archive_filter_lz4_free(struct archive_write_filter *);
84 static int archive_filter_lz4_open(struct archive_write_filter *);
85 static int archive_filter_lz4_options(struct archive_write_filter *,
86 		    const char *, const char *);
87 static int archive_filter_lz4_write(struct archive_write_filter *,
88 		    const void *, size_t);
89 
90 /*
91  * Add a lz4 compression filter to this write handle.
92  */
93 int
archive_write_add_filter_lz4(struct archive * _a)94 archive_write_add_filter_lz4(struct archive *_a)
95 {
96 	struct archive_write *a = (struct archive_write *)_a;
97 	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
98 	struct private_data *data;
99 
100 	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
101 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
102 
103 	data = calloc(1, sizeof(*data));
104 	if (data == NULL) {
105 		archive_set_error(&a->archive, ENOMEM, "Out of memory");
106 		return (ARCHIVE_FATAL);
107 	}
108 
109 	/*
110 	 * Setup default settings.
111 	 */
112 	data->compression_level = 1;
113 	data->version_number = 0x01;
114 	data->block_independence = 1;
115 	data->block_checksum = 0;
116 	data->stream_size = 0;
117 	data->stream_checksum = 1;
118 	data->preset_dictionary = 0;
119 	data->block_maximum_size = 7;
120 
121 	/*
122 	 * Setup a filter setting.
123 	 */
124 	f->data = data;
125 	f->options = &archive_filter_lz4_options;
126 	f->close = &archive_filter_lz4_close;
127 	f->free = &archive_filter_lz4_free;
128 	f->open = &archive_filter_lz4_open;
129 	f->code = ARCHIVE_FILTER_LZ4;
130 	f->name = "lz4";
131 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
132 	return (ARCHIVE_OK);
133 #else
134 	/*
135 	 * We don't have lz4 library, and execute external lz4 program
136 	 * instead.
137 	 */
138 	data->pdata = __archive_write_program_allocate("lz4");
139 	if (data->pdata == NULL) {
140 		free(data);
141 		archive_set_error(&a->archive, ENOMEM, "Out of memory");
142 		return (ARCHIVE_FATAL);
143 	}
144 	data->compression_level = 0;
145 	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
146 	    "Using external lz4 program");
147 	return (ARCHIVE_WARN);
148 #endif
149 }
150 
151 /*
152  * Set write options.
153  */
154 static int
archive_filter_lz4_options(struct archive_write_filter * f,const char * key,const char * value)155 archive_filter_lz4_options(struct archive_write_filter *f,
156     const char *key, const char *value)
157 {
158 	struct private_data *data = (struct private_data *)f->data;
159 
160 	if (strcmp(key, "compression-level") == 0) {
161 		int val;
162 		if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
163 		    value[1] != '\0') {
164 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
165 			    "compression-level invalid");
166 			return (ARCHIVE_FAILED);
167 		}
168 
169 #ifndef HAVE_LZ4HC_H
170 		if(val >= 3)
171 		{
172 			archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
173 				"High compression not included in this build");
174 			return (ARCHIVE_FATAL);
175 		}
176 #endif
177 		data->compression_level = val;
178 		return (ARCHIVE_OK);
179 	}
180 	if (strcmp(key, "stream-checksum") == 0) {
181 		data->stream_checksum = value != NULL;
182 		return (ARCHIVE_OK);
183 	}
184 	if (strcmp(key, "block-checksum") == 0) {
185 		data->block_checksum = value != NULL;
186 		return (ARCHIVE_OK);
187 	}
188 	if (strcmp(key, "block-size") == 0) {
189 		if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
190 		    value[1] != '\0') {
191 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
192 			    "block-size invalid");
193 			return (ARCHIVE_FAILED);
194 		}
195 		data->block_maximum_size = value[0] - '0';
196 		return (ARCHIVE_OK);
197 	}
198 	if (strcmp(key, "block-dependence") == 0) {
199 		data->block_independence = value == NULL;
200 		return (ARCHIVE_OK);
201 	}
202 
203 	/* Note: The "warn" return is just to inform the options
204 	 * supervisor that we didn't handle it.  It will generate
205 	 * a suitable error if no one used this option. */
206 	return (ARCHIVE_WARN);
207 }
208 
209 #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
210 /* Don't compile this if we don't have liblz4. */
211 
212 static int drive_compressor(struct archive_write_filter *, const char *,
213     size_t);
214 static int drive_compressor_independence(struct archive_write_filter *,
215     const char *, size_t);
216 static int drive_compressor_dependence(struct archive_write_filter *,
217     const char *, size_t);
218 static int lz4_write_stream_descriptor(struct archive_write_filter *);
219 static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
220     size_t);
221 
222 
223 /*
224  * Setup callback.
225  */
226 static int
archive_filter_lz4_open(struct archive_write_filter * f)227 archive_filter_lz4_open(struct archive_write_filter *f)
228 {
229 	struct private_data *data = (struct private_data *)f->data;
230 	size_t required_size;
231 	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
232 			   4 * 1024 * 1024 };
233 	size_t pre_block_size;
234 
235 	if (data->block_maximum_size < 4)
236 		data->block_size = bkmap[0];
237 	else
238 		data->block_size = bkmap[data->block_maximum_size - 4];
239 
240 	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
241 	if (data->out_buffer_size < required_size) {
242 		size_t bs = required_size, bpb;
243 		free(data->out_buffer);
244 		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
245 			/* Buffer size should be a multiple number of
246 			 * the of bytes per block for performance. */
247 			bpb = archive_write_get_bytes_per_block(f->archive);
248 			if (bpb > bs)
249 				bs = bpb;
250 			else if (bpb != 0) {
251 				bs += bpb;
252 				bs -= bs % bpb;
253 			}
254 		}
255 		data->out_block_size = bs;
256 		bs += required_size;
257 		data->out_buffer = malloc(bs);
258 		data->out = data->out_buffer;
259 		data->out_buffer_size = bs;
260 	}
261 
262 	pre_block_size = (data->block_independence)? 0: 64 * 1024;
263 	if (data->in_buffer_size < data->block_size + pre_block_size) {
264 		free(data->in_buffer_allocated);
265 		data->in_buffer_size = data->block_size;
266 		data->in_buffer_allocated =
267 		    malloc(data->in_buffer_size + pre_block_size);
268 		data->in_buffer = data->in_buffer_allocated + pre_block_size;
269 		if (!data->block_independence && data->compression_level >= 3)
270 		    data->in_buffer = data->in_buffer_allocated;
271 		data->in = data->in_buffer;
272 		data->in_buffer_size = data->block_size;
273 	}
274 
275 	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
276 		archive_set_error(f->archive, ENOMEM,
277 		    "Can't allocate data for compression buffer");
278 		return (ARCHIVE_FATAL);
279 	}
280 
281 	f->write = archive_filter_lz4_write;
282 
283 	return (ARCHIVE_OK);
284 }
285 
286 /*
287  * Write data to the out stream.
288  *
289  * Returns ARCHIVE_OK if all data written, error otherwise.
290  */
291 static int
archive_filter_lz4_write(struct archive_write_filter * f,const void * buff,size_t length)292 archive_filter_lz4_write(struct archive_write_filter *f,
293     const void *buff, size_t length)
294 {
295 	struct private_data *data = (struct private_data *)f->data;
296 	int ret = ARCHIVE_OK;
297 	const char *p;
298 	size_t remaining;
299 	ssize_t size;
300 
301 	/* If we haven't written a stream descriptor, we have to do it first. */
302 	if (!data->header_written) {
303 		ret = lz4_write_stream_descriptor(f);
304 		if (ret != ARCHIVE_OK)
305 			return (ret);
306 		data->header_written = 1;
307 	}
308 
309 	/* Update statistics */
310 	data->total_in += length;
311 
312 	p = (const char *)buff;
313 	remaining = length;
314 	while (remaining) {
315 		size_t l;
316 		/* Compress input data to output buffer */
317 		size = lz4_write_one_block(f, p, remaining);
318 		if (size < ARCHIVE_OK)
319 			return (ARCHIVE_FATAL);
320 		l = data->out - data->out_buffer;
321 		if (l >= data->out_block_size) {
322 			ret = __archive_write_filter(f->next_filter,
323 			    data->out_buffer, data->out_block_size);
324 			l -= data->out_block_size;
325 			memcpy(data->out_buffer,
326 			    data->out_buffer + data->out_block_size, l);
327 			data->out = data->out_buffer + l;
328 			if (ret < ARCHIVE_WARN)
329 				break;
330 		}
331 		p += size;
332 		remaining -= size;
333 	}
334 
335 	return (ret);
336 }
337 
338 /*
339  * Finish the compression.
340  */
341 static int
archive_filter_lz4_close(struct archive_write_filter * f)342 archive_filter_lz4_close(struct archive_write_filter *f)
343 {
344 	struct private_data *data = (struct private_data *)f->data;
345 	int ret;
346 
347 	/* Finish compression cycle. */
348 	ret = (int)lz4_write_one_block(f, NULL, 0);
349 	if (ret >= 0) {
350 		/*
351 		 * Write the last block and the end of the stream data.
352 		 */
353 
354 		/* Write End Of Stream. */
355 		memset(data->out, 0, 4); data->out += 4;
356 		/* Write Stream checksum if needed. */
357 		if (data->stream_checksum) {
358 			unsigned int checksum;
359 			checksum = __archive_xxhash.XXH32_digest(
360 					data->xxh32_state);
361 			data->xxh32_state = NULL;
362 			archive_le32enc(data->out, checksum);
363 			data->out += 4;
364 		}
365 		ret = __archive_write_filter(f->next_filter,
366 			    data->out_buffer, data->out - data->out_buffer);
367 	}
368 	return ret;
369 }
370 
371 static int
archive_filter_lz4_free(struct archive_write_filter * f)372 archive_filter_lz4_free(struct archive_write_filter *f)
373 {
374 	struct private_data *data = (struct private_data *)f->data;
375 
376 	if (data->lz4_stream != NULL) {
377 #ifdef HAVE_LZ4HC_H
378 		if (data->compression_level >= 3)
379 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
380 			LZ4_freeStreamHC(data->lz4_stream);
381 #else
382 			LZ4_freeHC(data->lz4_stream);
383 #endif
384 		else
385 #endif
386 #if LZ4_VERSION_MINOR >= 3
387 			LZ4_freeStream(data->lz4_stream);
388 #else
389 			LZ4_free(data->lz4_stream);
390 #endif
391 	}
392 	free(data->out_buffer);
393 	free(data->in_buffer_allocated);
394 	free(data->xxh32_state);
395 	free(data);
396 	f->data = NULL;
397 	return (ARCHIVE_OK);
398 }
399 
400 static int
lz4_write_stream_descriptor(struct archive_write_filter * f)401 lz4_write_stream_descriptor(struct archive_write_filter *f)
402 {
403 	struct private_data *data = (struct private_data *)f->data;
404 	uint8_t *sd;
405 
406 	sd = (uint8_t *)data->out;
407 	/* Write Magic Number. */
408 	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
409 	/* FLG */
410 	sd[4] = (data->version_number << 6)
411 	      | (data->block_independence << 5)
412 	      | (data->block_checksum << 4)
413 	      | (data->stream_size << 3)
414 	      | (data->stream_checksum << 2)
415 	      | (data->preset_dictionary << 0);
416 	/* BD */
417 	sd[5] = (data->block_maximum_size << 4);
418 	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
419 	data->out += 7;
420 	if (data->stream_checksum)
421 		data->xxh32_state = __archive_xxhash.XXH32_init(0);
422 	else
423 		data->xxh32_state = NULL;
424 	return (ARCHIVE_OK);
425 }
426 
427 static ssize_t
lz4_write_one_block(struct archive_write_filter * f,const char * p,size_t length)428 lz4_write_one_block(struct archive_write_filter *f, const char *p,
429     size_t length)
430 {
431 	struct private_data *data = (struct private_data *)f->data;
432 	ssize_t r;
433 
434 	if (p == NULL) {
435 		/* Compress remaining uncompressed data. */
436 		if (data->in_buffer == data->in)
437 			return 0;
438 		else {
439 			size_t l = data->in - data->in_buffer;
440 			r = drive_compressor(f, data->in_buffer, l);
441 			if (r == ARCHIVE_OK)
442 				r = (ssize_t)l;
443 		}
444 	} else if ((data->block_independence || data->compression_level < 3) &&
445 	    data->in_buffer == data->in && length >= data->block_size) {
446 		r = drive_compressor(f, p, data->block_size);
447 		if (r == ARCHIVE_OK)
448 			r = (ssize_t)data->block_size;
449 	} else {
450 		size_t remaining_size = data->in_buffer_size -
451 			(data->in - data->in_buffer);
452 		size_t l = (remaining_size > length)? length: remaining_size;
453 		memcpy(data->in, p, l);
454 		data->in += l;
455 		if (l == remaining_size) {
456 			r = drive_compressor(f, data->in_buffer,
457 			    data->block_size);
458 			if (r == ARCHIVE_OK)
459 				r = (ssize_t)l;
460 			data->in = data->in_buffer;
461 		} else
462 			r = (ssize_t)l;
463 	}
464 
465 	return (r);
466 }
467 
468 
469 /*
470  * Utility function to push input data through compressor, writing
471  * full output blocks as necessary.
472  *
473  * Note that this handles both the regular write case (finishing ==
474  * false) and the end-of-archive case (finishing == true).
475  */
476 static int
drive_compressor(struct archive_write_filter * f,const char * p,size_t length)477 drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
478 {
479 	struct private_data *data = (struct private_data *)f->data;
480 
481 	if (data->stream_checksum)
482 		__archive_xxhash.XXH32_update(data->xxh32_state,
483 			p, (int)length);
484 	if (data->block_independence)
485 		return drive_compressor_independence(f, p, length);
486 	else
487 		return drive_compressor_dependence(f, p, length);
488 }
489 
490 static int
drive_compressor_independence(struct archive_write_filter * f,const char * p,size_t length)491 drive_compressor_independence(struct archive_write_filter *f, const char *p,
492     size_t length)
493 {
494 	struct private_data *data = (struct private_data *)f->data;
495 	unsigned int outsize;
496 
497 #ifdef HAVE_LZ4HC_H
498 	if (data->compression_level >= 3)
499 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
500 		outsize = LZ4_compress_HC(p, data->out + 4,
501 		     (int)length, (int)data->block_size,
502 		    data->compression_level);
503 #else
504 		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
505 		    (int)length, (int)data->block_size,
506 		    data->compression_level);
507 #endif
508 	else
509 #endif
510 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
511 		outsize = LZ4_compress_default(p, data->out + 4,
512 		    (int)length, (int)data->block_size);
513 #else
514 		outsize = LZ4_compress_limitedOutput(p, data->out + 4,
515 		    (int)length, (int)data->block_size);
516 #endif
517 
518 	if (outsize) {
519 		/* The buffer is compressed. */
520 		archive_le32enc(data->out, outsize);
521 		data->out += 4;
522 	} else {
523 		/* The buffer is not compressed. The compressed size was
524 		 * bigger than its uncompressed size. */
525 		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
526 		data->out += 4;
527 		memcpy(data->out, p, length);
528 		outsize = (uint32_t)length;
529 	}
530 	data->out += outsize;
531 	if (data->block_checksum) {
532 		unsigned int checksum =
533 		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
534 		archive_le32enc(data->out, checksum);
535 		data->out += 4;
536 	}
537 	return (ARCHIVE_OK);
538 }
539 
540 static int
drive_compressor_dependence(struct archive_write_filter * f,const char * p,size_t length)541 drive_compressor_dependence(struct archive_write_filter *f, const char *p,
542     size_t length)
543 {
544 	struct private_data *data = (struct private_data *)f->data;
545 	int outsize;
546 
547 #define DICT_SIZE	(64 * 1024)
548 #ifdef HAVE_LZ4HC_H
549 	if (data->compression_level >= 3) {
550 		if (data->lz4_stream == NULL) {
551 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
552 			data->lz4_stream = LZ4_createStreamHC();
553 			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
554 #else
555 			data->lz4_stream =
556 			    LZ4_createHC(data->in_buffer_allocated);
557 #endif
558 			if (data->lz4_stream == NULL) {
559 				archive_set_error(f->archive, ENOMEM,
560 				    "Can't allocate data for compression"
561 				    " buffer");
562 				return (ARCHIVE_FATAL);
563 			}
564 		}
565 		else
566 			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
567 
568 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
569 		outsize = LZ4_compress_HC_continue(
570 		    data->lz4_stream, p, data->out + 4, (int)length,
571 		    (int)data->block_size);
572 #else
573 		outsize = LZ4_compressHC2_limitedOutput_continue(
574 		    data->lz4_stream, p, data->out + 4, (int)length,
575 		    (int)data->block_size, data->compression_level);
576 #endif
577 	} else
578 #endif
579 	{
580 		if (data->lz4_stream == NULL) {
581 			data->lz4_stream = LZ4_createStream();
582 			if (data->lz4_stream == NULL) {
583 				archive_set_error(f->archive, ENOMEM,
584 				    "Can't allocate data for compression"
585 				    " buffer");
586 				return (ARCHIVE_FATAL);
587 			}
588 		}
589 		else
590 			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
591 
592 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
593 		outsize = LZ4_compress_fast_continue(
594 		    data->lz4_stream, p, data->out + 4, (int)length,
595 		    (int)data->block_size, 1);
596 #else
597 		outsize = LZ4_compress_limitedOutput_continue(
598 		    data->lz4_stream, p, data->out + 4, (int)length,
599 		    (int)data->block_size);
600 #endif
601 	}
602 
603 	if (outsize) {
604 		/* The buffer is compressed. */
605 		archive_le32enc(data->out, outsize);
606 		data->out += 4;
607 	} else {
608 		/* The buffer is not compressed. The compressed size was
609 		 * bigger than its uncompressed size. */
610 		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
611 		data->out += 4;
612 		memcpy(data->out, p, length);
613 		outsize = (uint32_t)length;
614 	}
615 	data->out += outsize;
616 	if (data->block_checksum) {
617 		unsigned int checksum =
618 		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
619 		archive_le32enc(data->out, checksum);
620 		data->out += 4;
621 	}
622 
623 	if (length == data->block_size) {
624 #ifdef HAVE_LZ4HC_H
625 		if (data->compression_level >= 3) {
626 #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
627 			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
628 #else
629 			LZ4_slideInputBufferHC(data->lz4_stream);
630 #endif
631 			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
632 		}
633 		else
634 #endif
635 			LZ4_saveDict(data->lz4_stream,
636 			    data->in_buffer_allocated, DICT_SIZE);
637 #undef DICT_SIZE
638 	}
639 	return (ARCHIVE_OK);
640 }
641 
642 #else /* HAVE_LIBLZ4 */
643 
644 static int
archive_filter_lz4_open(struct archive_write_filter * f)645 archive_filter_lz4_open(struct archive_write_filter *f)
646 {
647 	struct private_data *data = (struct private_data *)f->data;
648 	struct archive_string as;
649 	int r;
650 
651 	archive_string_init(&as);
652 	archive_strcpy(&as, "lz4 -z -q -q");
653 
654 	/* Specify a compression level. */
655 	if (data->compression_level > 0) {
656 		archive_strcat(&as, " -");
657 		archive_strappend_char(&as, '0' + data->compression_level);
658 	}
659 	/* Specify a block size. */
660 	archive_strcat(&as, " -B");
661 	archive_strappend_char(&as, '0' + data->block_maximum_size);
662 
663 	if (data->block_checksum)
664 		archive_strcat(&as, " -BX");
665 	if (data->stream_checksum == 0)
666 		archive_strcat(&as, " --no-frame-crc");
667 	if (data->block_independence == 0)
668 		archive_strcat(&as, " -BD");
669 
670 	f->write = archive_filter_lz4_write;
671 
672 	r = __archive_write_program_open(f, data->pdata, as.s);
673 	archive_string_free(&as);
674 	return (r);
675 }
676 
677 static int
archive_filter_lz4_write(struct archive_write_filter * f,const void * buff,size_t length)678 archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
679     size_t length)
680 {
681 	struct private_data *data = (struct private_data *)f->data;
682 
683 	return __archive_write_program_write(f, data->pdata, buff, length);
684 }
685 
686 static int
archive_filter_lz4_close(struct archive_write_filter * f)687 archive_filter_lz4_close(struct archive_write_filter *f)
688 {
689 	struct private_data *data = (struct private_data *)f->data;
690 
691 	return __archive_write_program_close(f, data->pdata);
692 }
693 
694 static int
archive_filter_lz4_free(struct archive_write_filter * f)695 archive_filter_lz4_free(struct archive_write_filter *f)
696 {
697 	struct private_data *data = (struct private_data *)f->data;
698 
699 	__archive_write_program_free(data->pdata);
700 	free(data);
701 	return (ARCHIVE_OK);
702 }
703 
704 #endif /* HAVE_LIBLZ4 */
705