Blame libarchive/archive_write_add_filter_lz4.c

Packit Service 1d0348
/*-
Packit Service 1d0348
 * Copyright (c) 2014 Michihiro NAKAJIMA
Packit Service 1d0348
 * All rights reserved.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Redistribution and use in source and binary forms, with or without
Packit Service 1d0348
 * modification, are permitted provided that the following conditions
Packit Service 1d0348
 * are met:
Packit Service 1d0348
 * 1. Redistributions of source code must retain the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer.
Packit Service 1d0348
 * 2. Redistributions in binary form must reproduce the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer in the
Packit Service 1d0348
 *    documentation and/or other materials provided with the distribution.
Packit Service 1d0348
 *
Packit Service 1d0348
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit Service 1d0348
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 1d0348
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit Service 1d0348
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 1d0348
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit Service 1d0348
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 1d0348
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 1d0348
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 1d0348
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit Service 1d0348
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
#include "archive_platform.h"
Packit Service 1d0348
Packit Service 1d0348
__FBSDID("$FreeBSD$");
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_ERRNO_H
Packit Service 1d0348
#include <errno.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#include <stdio.h>
Packit Service 1d0348
#ifdef HAVE_STDLIB_H
Packit Service 1d0348
#include <stdlib.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STRING_H
Packit Service 1d0348
#include <string.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_LZ4_H
Packit Service 1d0348
#include <lz4.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_LZ4HC_H
Packit Service 1d0348
#include <lz4hc.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
#include "archive.h"
Packit Service 1d0348
#include "archive_endian.h"
Packit Service 1d0348
#include "archive_private.h"
Packit Service 1d0348
#include "archive_write_private.h"
Packit Service 1d0348
#include "archive_xxhash.h"
Packit Service 1d0348
Packit Service 1d0348
#define LZ4_MAGICNUMBER	0x184d2204
Packit Service 1d0348
Packit Service 1d0348
struct private_data {
Packit Service 1d0348
	int		 compression_level;
Packit Service 1d0348
	unsigned	 header_written:1;
Packit Service 1d0348
	unsigned	 version_number:1;
Packit Service 1d0348
	unsigned	 block_independence:1;
Packit Service 1d0348
	unsigned	 block_checksum:1;
Packit Service 1d0348
	unsigned	 stream_size:1;
Packit Service 1d0348
	unsigned	 stream_checksum:1;
Packit Service 1d0348
	unsigned	 preset_dictionary:1;
Packit Service 1d0348
	unsigned	 block_maximum_size:3;
Packit Service 1d0348
#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
Packit Service 1d0348
	int64_t		 total_in;
Packit Service 1d0348
	char		*out;
Packit Service 1d0348
	char		*out_buffer;
Packit Service 1d0348
	size_t		 out_buffer_size;
Packit Service 1d0348
	size_t		 out_block_size;
Packit Service 1d0348
	char		*in;
Packit Service 1d0348
	char		*in_buffer_allocated;
Packit Service 1d0348
	char		*in_buffer;
Packit Service 1d0348
	size_t		 in_buffer_size;
Packit Service 1d0348
	size_t		 block_size;
Packit Service 1d0348
Packit Service 1d0348
	void		*xxh32_state;
Packit Service 1d0348
	void		*lz4_stream;
Packit Service 1d0348
#else
Packit Service 1d0348
	struct archive_write_program_data *pdata;
Packit Service 1d0348
#endif
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
static int archive_filter_lz4_close(struct archive_write_filter *);
Packit Service 1d0348
static int archive_filter_lz4_free(struct archive_write_filter *);
Packit Service 1d0348
static int archive_filter_lz4_open(struct archive_write_filter *);
Packit Service 1d0348
static int archive_filter_lz4_options(struct archive_write_filter *,
Packit Service 1d0348
		    const char *, const char *);
Packit Service 1d0348
static int archive_filter_lz4_write(struct archive_write_filter *,
Packit Service 1d0348
		    const void *, size_t);
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Add a lz4 compression filter to this write handle.
Packit Service 1d0348
 */
Packit Service 1d0348
int
Packit Service 1d0348
archive_write_add_filter_lz4(struct archive *_a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_write *a = (struct archive_write *)_a;
Packit Service 1d0348
	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
Packit Service 1d0348
	struct private_data *data;
Packit Service 1d0348
Packit Service 1d0348
	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
Packit Service 1d0348
	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
Packit Service 1d0348
Packit Service 1d0348
	data = calloc(1, sizeof(*data));
Packit Service 1d0348
	if (data == NULL) {
Packit Service 1d0348
		archive_set_error(&a->archive, ENOMEM, "Out of memory");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * Setup default settings.
Packit Service 1d0348
	 */
Packit Service 1d0348
	data->compression_level = 1;
Packit Service 1d0348
	data->version_number = 0x01;
Packit Service 1d0348
	data->block_independence = 1;
Packit Service 1d0348
	data->block_checksum = 0;
Packit Service 1d0348
	data->stream_size = 0;
Packit Service 1d0348
	data->stream_checksum = 1;
Packit Service 1d0348
	data->preset_dictionary = 0;
Packit Service 1d0348
	data->block_maximum_size = 7;
Packit Service 1d0348
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * Setup a filter setting.
Packit Service 1d0348
	 */
Packit Service 1d0348
	f->data = data;
Packit Service 1d0348
	f->options = &archive_filter_lz4_options;
Packit Service 1d0348
	f->close = &archive_filter_lz4_close;
Packit Service 1d0348
	f->free = &archive_filter_lz4_free;
Packit Service 1d0348
	f->open = &archive_filter_lz4_open;
Packit Service 1d0348
	f->code = ARCHIVE_FILTER_LZ4;
Packit Service 1d0348
	f->name = "lz4";
Packit Service 1d0348
#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
#else
Packit Service 1d0348
	/*
Packit Service 1d0348
	 * We don't have lz4 library, and execute external lz4 program
Packit Service 1d0348
	 * instead.
Packit Service 1d0348
	 */
Packit Service 1d0348
	data->pdata = __archive_write_program_allocate("lz4");
Packit Service 1d0348
	if (data->pdata == NULL) {
Packit Service 1d0348
		free(data);
Packit Service 1d0348
		archive_set_error(&a->archive, ENOMEM, "Out of memory");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
	data->compression_level = 0;
Packit Service 1d0348
	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
	    "Using external lz4 program");
Packit Service 1d0348
	return (ARCHIVE_WARN);
Packit Service 1d0348
#endif
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Set write options.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_options(struct archive_write_filter *f,
Packit Service 1d0348
    const char *key, const char *value)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	if (strcmp(key, "compression-level") == 0) {
Packit Service 1d0348
		int val;
Packit Service 1d0348
		if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
Packit Service 1d0348
		    value[1] != '\0')
Packit Service 1d0348
			return (ARCHIVE_WARN);
Packit Service 1d0348
Packit Service 1d0348
#ifndef HAVE_LZ4HC_H
Packit Service 1d0348
		if(val >= 3)
Packit Service 1d0348
		{
Packit Service 1d0348
			archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
Packit Service 1d0348
				"High compression not included in this build");
Packit Service 1d0348
			return (ARCHIVE_FATAL);
Packit Service 1d0348
		}
Packit Service 1d0348
#endif
Packit Service 1d0348
		data->compression_level = val;
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (strcmp(key, "stream-checksum") == 0) {
Packit Service 1d0348
		data->stream_checksum = value != NULL;
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (strcmp(key, "block-checksum") == 0) {
Packit Service 1d0348
		data->block_checksum = value != NULL;
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (strcmp(key, "block-size") == 0) {
Packit Service 1d0348
		if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
Packit Service 1d0348
		    value[1] != '\0')
Packit Service 1d0348
			return (ARCHIVE_WARN);
Packit Service 1d0348
		data->block_maximum_size = value[0] - '0';
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (strcmp(key, "block-dependence") == 0) {
Packit Service 1d0348
		data->block_independence = value == NULL;
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Note: The "warn" return is just to inform the options
Packit Service 1d0348
	 * supervisor that we didn't handle it.  It will generate
Packit Service 1d0348
	 * a suitable error if no one used this option. */
Packit Service 1d0348
	return (ARCHIVE_WARN);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
Packit Service 1d0348
/* Don't compile this if we don't have liblz4. */
Packit Service 1d0348
Packit Service 1d0348
static int drive_compressor(struct archive_write_filter *, const char *,
Packit Service 1d0348
    size_t);
Packit Service 1d0348
static int drive_compressor_independence(struct archive_write_filter *,
Packit Service 1d0348
    const char *, size_t);
Packit Service 1d0348
static int drive_compressor_dependence(struct archive_write_filter *,
Packit Service 1d0348
    const char *, size_t);
Packit Service 1d0348
static int lz4_write_stream_descriptor(struct archive_write_filter *);
Packit Service 1d0348
static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
Packit Service 1d0348
    size_t);
Packit Service 1d0348
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Setup callback.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_open(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	int ret;
Packit Service 1d0348
	size_t required_size;
Packit Service 1d0348
	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
Packit Service 1d0348
			   4 * 1024 * 1024 };
Packit Service 1d0348
	size_t pre_block_size;
Packit Service 1d0348
Packit Service 1d0348
	ret = __archive_write_open_filter(f->next_filter);
Packit Service 1d0348
	if (ret != 0)
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
Packit Service 1d0348
	if (data->block_maximum_size < 4)
Packit Service 1d0348
		data->block_size = bkmap[0];
Packit Service 1d0348
	else
Packit Service 1d0348
		data->block_size = bkmap[data->block_maximum_size - 4];
Packit Service 1d0348
Packit Service 1d0348
	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
Packit Service 1d0348
	if (data->out_buffer_size < required_size) {
Packit Service 1d0348
		size_t bs = required_size, bpb;
Packit Service 1d0348
		free(data->out_buffer);
Packit Service 1d0348
		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
Packit Service 1d0348
			/* Buffer size should be a multiple number of
Packit Service 1d0348
			 * the of bytes per block for performance. */
Packit Service 1d0348
			bpb = archive_write_get_bytes_per_block(f->archive);
Packit Service 1d0348
			if (bpb > bs)
Packit Service 1d0348
				bs = bpb;
Packit Service 1d0348
			else if (bpb != 0) {
Packit Service 1d0348
				bs += bpb;
Packit Service 1d0348
				bs -= bs % bpb;
Packit Service 1d0348
			}
Packit Service 1d0348
		}
Packit Service 1d0348
		data->out_block_size = bs;
Packit Service 1d0348
		bs += required_size;
Packit Service 1d0348
		data->out_buffer = malloc(bs);
Packit Service 1d0348
		data->out = data->out_buffer;
Packit Service 1d0348
		data->out_buffer_size = bs;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	pre_block_size = (data->block_independence)? 0: 64 * 1024;
Packit Service 1d0348
	if (data->in_buffer_size < data->block_size + pre_block_size) {
Packit Service 1d0348
		free(data->in_buffer_allocated);
Packit Service 1d0348
		data->in_buffer_size = data->block_size;
Packit Service 1d0348
		data->in_buffer_allocated =
Packit Service 1d0348
		    malloc(data->in_buffer_size + pre_block_size);
Packit Service 1d0348
		data->in_buffer = data->in_buffer_allocated + pre_block_size;
Packit Service 1d0348
		if (!data->block_independence && data->compression_level >= 3)
Packit Service 1d0348
		    data->in_buffer = data->in_buffer_allocated;
Packit Service 1d0348
		data->in = data->in_buffer;
Packit Service 1d0348
		data->in_buffer_size = data->block_size;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
Packit Service 1d0348
		archive_set_error(f->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate data for compression buffer");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	f->write = archive_filter_lz4_write;
Packit Service 1d0348
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Write data to the out stream.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Returns ARCHIVE_OK if all data written, error otherwise.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_write(struct archive_write_filter *f,
Packit Service 1d0348
    const void *buff, size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	int ret = ARCHIVE_OK;
Packit Service 1d0348
	const char *p;
Packit Service 1d0348
	size_t remaining;
Packit Service 1d0348
	ssize_t size;
Packit Service 1d0348
Packit Service 1d0348
	/* If we haven't written a stream descriptor, we have to do it first. */
Packit Service 1d0348
	if (!data->header_written) {
Packit Service 1d0348
		ret = lz4_write_stream_descriptor(f);
Packit Service 1d0348
		if (ret != ARCHIVE_OK)
Packit Service 1d0348
			return (ret);
Packit Service 1d0348
		data->header_written = 1;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Update statistics */
Packit Service 1d0348
	data->total_in += length;
Packit Service 1d0348
Packit Service 1d0348
	p = (const char *)buff;
Packit Service 1d0348
	remaining = length;
Packit Service 1d0348
	while (remaining) {
Packit Service 1d0348
		size_t l;
Packit Service 1d0348
		/* Compress input data to output buffer */
Packit Service 1d0348
		size = lz4_write_one_block(f, p, remaining);
Packit Service 1d0348
		if (size < ARCHIVE_OK)
Packit Service 1d0348
			return (ARCHIVE_FATAL);
Packit Service 1d0348
		l = data->out - data->out_buffer;
Packit Service 1d0348
		if (l >= data->out_block_size) {
Packit Service 1d0348
			ret = __archive_write_filter(f->next_filter,
Packit Service 1d0348
			    data->out_buffer, data->out_block_size);
Packit Service 1d0348
			l -= data->out_block_size;
Packit Service 1d0348
			memcpy(data->out_buffer,
Packit Service 1d0348
			    data->out_buffer + data->out_block_size, l);
Packit Service 1d0348
			data->out = data->out_buffer + l;
Packit Service 1d0348
			if (ret < ARCHIVE_WARN)
Packit Service 1d0348
				break;
Packit Service 1d0348
		}
Packit Service 1d0348
		p += size;
Packit Service 1d0348
		remaining -= size;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Finish the compression.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_close(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	int ret, r1;
Packit Service 1d0348
Packit Service 1d0348
	/* Finish compression cycle. */
Packit Service 1d0348
	ret = (int)lz4_write_one_block(f, NULL, 0);
Packit Service 1d0348
	if (ret >= 0) {
Packit Service 1d0348
		/*
Packit Service 1d0348
		 * Write the last block and the end of the stream data.
Packit Service 1d0348
		 */
Packit Service 1d0348
Packit Service 1d0348
		/* Write End Of Stream. */
Packit Service 1d0348
		memset(data->out, 0, 4); data->out += 4;
Packit Service 1d0348
		/* Write Stream checksum if needed. */
Packit Service 1d0348
		if (data->stream_checksum) {
Packit Service 1d0348
			unsigned int checksum;
Packit Service 1d0348
			checksum = __archive_xxhash.XXH32_digest(
Packit Service 1d0348
					data->xxh32_state);
Packit Service 1d0348
			data->xxh32_state = NULL;
Packit Service 1d0348
			archive_le32enc(data->out, checksum);
Packit Service 1d0348
			data->out += 4;
Packit Service 1d0348
		}
Packit Service 1d0348
		ret = __archive_write_filter(f->next_filter,
Packit Service 1d0348
			    data->out_buffer, data->out - data->out_buffer);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	r1 = __archive_write_close_filter(f->next_filter);
Packit Service 1d0348
	return (r1 < ret ? r1 : ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_free(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	if (data->lz4_stream != NULL) {
Packit Service 1d0348
#ifdef HAVE_LZ4HC_H
Packit Service 1d0348
		if (data->compression_level >= 3)
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
			LZ4_freeStreamHC(data->lz4_stream);
Packit Service 1d0348
#else
Packit Service 1d0348
			LZ4_freeHC(data->lz4_stream);
Packit Service 1d0348
#endif
Packit Service 1d0348
		else
Packit Service 1d0348
#endif
Packit Service 1d0348
#if LZ4_VERSION_MINOR >= 3
Packit Service 1d0348
			LZ4_freeStream(data->lz4_stream);
Packit Service 1d0348
#else
Packit Service 1d0348
			LZ4_free(data->lz4_stream);
Packit Service 1d0348
#endif
Packit Service 1d0348
	}
Packit Service 1d0348
	free(data->out_buffer);
Packit Service 1d0348
	free(data->in_buffer_allocated);
Packit Service 1d0348
	free(data->xxh32_state);
Packit Service 1d0348
	free(data);
Packit Service 1d0348
	f->data = NULL;
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
lz4_write_stream_descriptor(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	uint8_t *sd;
Packit Service 1d0348
Packit Service 1d0348
	sd = (uint8_t *)data->out;
Packit Service 1d0348
	/* Write Magic Number. */
Packit Service 1d0348
	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
Packit Service 1d0348
	/* FLG */
Packit Service 1d0348
	sd[4] = (data->version_number << 6)
Packit Service 1d0348
	      | (data->block_independence << 5)
Packit Service 1d0348
	      | (data->block_checksum << 4)
Packit Service 1d0348
	      | (data->stream_size << 3)
Packit Service 1d0348
	      | (data->stream_checksum << 2)
Packit Service 1d0348
	      | (data->preset_dictionary << 0);
Packit Service 1d0348
	/* BD */
Packit Service 1d0348
	sd[5] = (data->block_maximum_size << 4);
Packit Service 1d0348
	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
Packit Service 1d0348
	data->out += 7;
Packit Service 1d0348
	if (data->stream_checksum)
Packit Service 1d0348
		data->xxh32_state = __archive_xxhash.XXH32_init(0);
Packit Service 1d0348
	else
Packit Service 1d0348
		data->xxh32_state = NULL;
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static ssize_t
Packit Service 1d0348
lz4_write_one_block(struct archive_write_filter *f, const char *p,
Packit Service 1d0348
    size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	ssize_t r;
Packit Service 1d0348
Packit Service 1d0348
	if (p == NULL) {
Packit Service 1d0348
		/* Compress remaining uncompressed data. */
Packit Service 1d0348
		if (data->in_buffer == data->in)
Packit Service 1d0348
			return 0;
Packit Service 1d0348
		else {
Packit Service 1d0348
			size_t l = data->in - data->in_buffer;
Packit Service 1d0348
			r = drive_compressor(f, data->in_buffer, l);
Packit Service 1d0348
			if (r == ARCHIVE_OK)
Packit Service 1d0348
				r = (ssize_t)l;
Packit Service 1d0348
		}
Packit Service 1d0348
	} else if ((data->block_independence || data->compression_level < 3) &&
Packit Service 1d0348
	    data->in_buffer == data->in && length >= data->block_size) {
Packit Service 1d0348
		r = drive_compressor(f, p, data->block_size);
Packit Service 1d0348
		if (r == ARCHIVE_OK)
Packit Service 1d0348
			r = (ssize_t)data->block_size;
Packit Service 1d0348
	} else {
Packit Service 1d0348
		size_t remaining_size = data->in_buffer_size -
Packit Service 1d0348
			(data->in - data->in_buffer);
Packit Service 1d0348
		size_t l = (remaining_size > length)? length: remaining_size;
Packit Service 1d0348
		memcpy(data->in, p, l);
Packit Service 1d0348
		data->in += l;
Packit Service 1d0348
		if (l == remaining_size) {
Packit Service 1d0348
			r = drive_compressor(f, data->in_buffer,
Packit Service 1d0348
			    data->block_size);
Packit Service 1d0348
			if (r == ARCHIVE_OK)
Packit Service 1d0348
				r = (ssize_t)l;
Packit Service 1d0348
			data->in = data->in_buffer;
Packit Service 1d0348
		} else
Packit Service 1d0348
			r = (ssize_t)l;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	return (r);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Utility function to push input data through compressor, writing
Packit Service 1d0348
 * full output blocks as necessary.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Note that this handles both the regular write case (finishing ==
Packit Service 1d0348
 * false) and the end-of-archive case (finishing == true).
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	if (data->stream_checksum)
Packit Service 1d0348
		__archive_xxhash.XXH32_update(data->xxh32_state,
Packit Service 1d0348
			p, (int)length);
Packit Service 1d0348
	if (data->block_independence)
Packit Service 1d0348
		return drive_compressor_independence(f, p, length);
Packit Service 1d0348
	else
Packit Service 1d0348
		return drive_compressor_dependence(f, p, length);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
drive_compressor_independence(struct archive_write_filter *f, const char *p,
Packit Service 1d0348
    size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	unsigned int outsize;
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_LZ4HC_H
Packit Service 1d0348
	if (data->compression_level >= 3)
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
		outsize = LZ4_compress_HC(p, data->out + 4,
Packit Service 1d0348
		     (int)length, (int)data->block_size,
Packit Service 1d0348
		    data->compression_level);
Packit Service 1d0348
#else
Packit Service 1d0348
		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
Packit Service 1d0348
		    (int)length, (int)data->block_size,
Packit Service 1d0348
		    data->compression_level);
Packit Service 1d0348
#endif
Packit Service 1d0348
	else
Packit Service 1d0348
#endif
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
		outsize = LZ4_compress_default(p, data->out + 4,
Packit Service 1d0348
		    (int)length, (int)data->block_size);
Packit Service 1d0348
#else
Packit Service 1d0348
		outsize = LZ4_compress_limitedOutput(p, data->out + 4,
Packit Service 1d0348
		    (int)length, (int)data->block_size);
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
	if (outsize) {
Packit Service 1d0348
		/* The buffer is compressed. */
Packit Service 1d0348
		archive_le32enc(data->out, outsize);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
	} else {
Packit Service 1d0348
		/* The buffer is not compressed. The compressed size was
Packit Service 1d0348
		 * bigger than its uncompressed size. */
Packit Service 1d0348
		archive_le32enc(data->out, length | 0x80000000);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
		memcpy(data->out, p, length);
Packit Service 1d0348
		outsize = length;
Packit Service 1d0348
	}
Packit Service 1d0348
	data->out += outsize;
Packit Service 1d0348
	if (data->block_checksum) {
Packit Service 1d0348
		unsigned int checksum =
Packit Service 1d0348
		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
Packit Service 1d0348
		archive_le32enc(data->out, checksum);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
	}
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
drive_compressor_dependence(struct archive_write_filter *f, const char *p,
Packit Service 1d0348
    size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	int outsize;
Packit Service 1d0348
Packit Service 1d0348
#define DICT_SIZE	(64 * 1024)
Packit Service 1d0348
#ifdef HAVE_LZ4HC_H
Packit Service 1d0348
	if (data->compression_level >= 3) {
Packit Service 1d0348
		if (data->lz4_stream == NULL) {
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
			data->lz4_stream = LZ4_createStreamHC();
Packit Service 1d0348
			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
Packit Service 1d0348
#else
Packit Service 1d0348
			data->lz4_stream =
Packit Service 1d0348
			    LZ4_createHC(data->in_buffer_allocated);
Packit Service 1d0348
#endif
Packit Service 1d0348
			if (data->lz4_stream == NULL) {
Packit Service 1d0348
				archive_set_error(f->archive, ENOMEM,
Packit Service 1d0348
				    "Can't allocate data for compression"
Packit Service 1d0348
				    " buffer");
Packit Service 1d0348
				return (ARCHIVE_FATAL);
Packit Service 1d0348
			}
Packit Service 1d0348
		}
Packit Service 1d0348
		else
Packit Service 1d0348
			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
Packit Service 1d0348
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
		outsize = LZ4_compress_HC_continue(
Packit Service 1d0348
		    data->lz4_stream, p, data->out + 4, (int)length,
Packit Service 1d0348
		    (int)data->block_size);
Packit Service 1d0348
#else
Packit Service 1d0348
		outsize = LZ4_compressHC2_limitedOutput_continue(
Packit Service 1d0348
		    data->lz4_stream, p, data->out + 4, (int)length,
Packit Service 1d0348
		    (int)data->block_size, data->compression_level);
Packit Service 1d0348
#endif
Packit Service 1d0348
	} else
Packit Service 1d0348
#endif
Packit Service 1d0348
	{
Packit Service 1d0348
		if (data->lz4_stream == NULL) {
Packit Service 1d0348
			data->lz4_stream = LZ4_createStream();
Packit Service 1d0348
			if (data->lz4_stream == NULL) {
Packit Service 1d0348
				archive_set_error(f->archive, ENOMEM,
Packit Service 1d0348
				    "Can't allocate data for compression"
Packit Service 1d0348
				    " buffer");
Packit Service 1d0348
				return (ARCHIVE_FATAL);
Packit Service 1d0348
			}
Packit Service 1d0348
		}
Packit Service 1d0348
		else
Packit Service 1d0348
			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
Packit Service 1d0348
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
		outsize = LZ4_compress_fast_continue(
Packit Service 1d0348
		    data->lz4_stream, p, data->out + 4, (int)length,
Packit Service 1d0348
		    (int)data->block_size, 1);
Packit Service 1d0348
#else
Packit Service 1d0348
		outsize = LZ4_compress_limitedOutput_continue(
Packit Service 1d0348
		    data->lz4_stream, p, data->out + 4, (int)length,
Packit Service 1d0348
		    (int)data->block_size);
Packit Service 1d0348
#endif
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (outsize) {
Packit Service 1d0348
		/* The buffer is compressed. */
Packit Service 1d0348
		archive_le32enc(data->out, outsize);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
	} else {
Packit Service 1d0348
		/* The buffer is not compressed. The compressed size was
Packit Service 1d0348
		 * bigger than its uncompressed size. */
Packit Service 1d0348
		archive_le32enc(data->out, length | 0x80000000);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
		memcpy(data->out, p, length);
Packit Service 1d0348
		outsize = length;
Packit Service 1d0348
	}
Packit Service 1d0348
	data->out += outsize;
Packit Service 1d0348
	if (data->block_checksum) {
Packit Service 1d0348
		unsigned int checksum =
Packit Service 1d0348
		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
Packit Service 1d0348
		archive_le32enc(data->out, checksum);
Packit Service 1d0348
		data->out += 4;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	if (length == data->block_size) {
Packit Service 1d0348
#ifdef HAVE_LZ4HC_H
Packit Service 1d0348
		if (data->compression_level >= 3) {
Packit Service 1d0348
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
Packit Service 1d0348
			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
Packit Service 1d0348
#else
Packit Service 1d0348
			LZ4_slideInputBufferHC(data->lz4_stream);
Packit Service 1d0348
#endif
Packit Service 1d0348
			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
Packit Service 1d0348
		}
Packit Service 1d0348
		else
Packit Service 1d0348
#endif
Packit Service 1d0348
			LZ4_saveDict(data->lz4_stream,
Packit Service 1d0348
			    data->in_buffer_allocated, DICT_SIZE);
Packit Service 1d0348
#undef DICT_SIZE
Packit Service 1d0348
	}
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
#else /* HAVE_LIBLZ4 */
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_open(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
	struct archive_string as;
Packit Service 1d0348
	int r;
Packit Service 1d0348
Packit Service 1d0348
	archive_string_init(&as);
Packit Service 1d0348
	archive_strcpy(&as, "lz4 -z -q -q");
Packit Service 1d0348
Packit Service 1d0348
	/* Specify a compression level. */
Packit Service 1d0348
	if (data->compression_level > 0) {
Packit Service 1d0348
		archive_strcat(&as, " -");
Packit Service 1d0348
		archive_strappend_char(&as, '0' + data->compression_level);
Packit Service 1d0348
	}
Packit Service 1d0348
	/* Specify a block size. */
Packit Service 1d0348
	archive_strcat(&as, " -B");
Packit Service 1d0348
	archive_strappend_char(&as, '0' + data->block_maximum_size);
Packit Service 1d0348
Packit Service 1d0348
	if (data->block_checksum)
Packit Service 1d0348
		archive_strcat(&as, " -BX");
Packit Service 1d0348
	if (data->stream_checksum == 0)
Packit Service 1d0348
		archive_strcat(&as, " --no-frame-crc");
Packit Service 1d0348
	if (data->block_independence == 0)
Packit Service 1d0348
		archive_strcat(&as, " -BD");
Packit Service 1d0348
Packit Service 1d0348
	f->write = archive_filter_lz4_write;
Packit Service 1d0348
Packit Service 1d0348
	r = __archive_write_program_open(f, data->pdata, as.s);
Packit Service 1d0348
	archive_string_free(&as);
Packit Service 1d0348
	return (r);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
Packit Service 1d0348
    size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	return __archive_write_program_write(f, data->pdata, buff, length);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_close(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	return __archive_write_program_close(f, data->pdata);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_lz4_free(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_data *data = (struct private_data *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	__archive_write_program_free(data->pdata);
Packit Service 1d0348
	free(data);
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
#endif /* HAVE_LIBLZ4 */