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