2010-03-29 01:07:08 +02:00
|
|
|
/*
|
2010-12-15 23:45:21 +01:00
|
|
|
Copyright (C) 2006-2010 Con Kolivas
|
|
|
|
|
Copyright (C) 1998 Andrew Tridgell
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2010-12-15 23:45:21 +01:00
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2010-03-29 01:07:08 +02:00
|
|
|
*/
|
|
|
|
|
/* multiplex N streams into a file - the streams are passed
|
|
|
|
|
through different compressors */
|
|
|
|
|
|
|
|
|
|
#include "rzip.h"
|
|
|
|
|
|
2010-12-08 11:25:00 +01:00
|
|
|
#define STREAM_BUFSIZE (1024 * 1024 * 10)
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
struct compress_thread{
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *s_buf; /* Uncompressed buffer -> Compressed buffer */
|
2010-11-10 10:56:17 +01:00
|
|
|
uchar c_type; /* Compression type */
|
|
|
|
|
i64 s_len; /* Data length uncompressed */
|
|
|
|
|
i64 c_len; /* Data length compressed */
|
|
|
|
|
sem_t complete; /* Signal when this thread has finished */
|
2010-11-12 15:26:09 +01:00
|
|
|
sem_t free; /* This thread no longer exists */
|
|
|
|
|
int wait_on; /* Which thread has to complete before this can write its data */
|
|
|
|
|
struct stream_info *sinfo;
|
|
|
|
|
int stream;
|
2010-11-10 10:56:17 +01:00
|
|
|
} *cthread;
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
struct uncomp_thread{
|
|
|
|
|
uchar *s_buf;
|
|
|
|
|
i64 u_len, c_len;
|
2010-11-24 10:12:19 +01:00
|
|
|
i64 last_head;
|
2010-11-16 11:25:32 +01:00
|
|
|
uchar c_type;
|
|
|
|
|
sem_t complete;
|
|
|
|
|
sem_t ready; /* Taken this thread's data so it can die */
|
2011-02-07 22:55:36 +01:00
|
|
|
int busy;
|
2010-11-16 11:25:32 +01:00
|
|
|
int stream;
|
|
|
|
|
} *ucthread;
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
void init_sem(sem_t *sem)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(sem_init(sem, 0, 0)))
|
|
|
|
|
fatal("sem_init\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void post_sem(sem_t *s)
|
|
|
|
|
{
|
|
|
|
|
retry:
|
|
|
|
|
if (unlikely((sem_post(s)) == -1)) {
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
goto retry;
|
|
|
|
|
fatal("sem_post failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void wait_sem(sem_t *s)
|
|
|
|
|
{
|
|
|
|
|
retry:
|
|
|
|
|
if (unlikely((sem_wait(s)) == -1)) {
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
goto retry;
|
|
|
|
|
fatal("sem_wait failed");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void destroy_sem(sem_t *s)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(sem_destroy(s)))
|
|
|
|
|
fatal("sem_destroy failed\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void create_pthread(pthread_t * thread, pthread_attr_t * attr,
|
|
|
|
|
void * (*start_routine)(void *), void *arg)
|
|
|
|
|
{
|
|
|
|
|
if (pthread_create(thread, attr, start_routine, arg))
|
|
|
|
|
fatal("pthread_create");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void join_pthread(pthread_t th, void **thread_return)
|
|
|
|
|
{
|
|
|
|
|
if (pthread_join(th, thread_return))
|
|
|
|
|
fatal("pthread_join");
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
/* just to keep things clean, declare function here
|
|
|
|
|
* but move body to the end since it's a work function
|
|
|
|
|
*/
|
2010-11-10 10:56:17 +01:00
|
|
|
static int lzo_compresses(uchar *s_buf, i64 s_len);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-03 03:14:46 +01:00
|
|
|
static inline FILE *fake_fmemopen(void *buf, size_t buflen, char *mode)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
|
|
|
|
FILE *in;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(strcmp(mode, "r")))
|
|
|
|
|
fatal("fake_fmemopen only supports mode \"r\".");
|
2010-03-29 01:07:08 +02:00
|
|
|
in = tmpfile();
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!in))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (unlikely(fwrite(buf, buflen, 1, in) != 1))
|
|
|
|
|
return NULL;
|
2010-03-29 01:07:08 +02:00
|
|
|
rewind(in);
|
|
|
|
|
return in;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 03:14:46 +01:00
|
|
|
static inline FILE *fake_open_memstream(char **buf, size_t *length)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
|
|
|
|
FILE *out;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(buf == NULL || length == NULL))
|
|
|
|
|
fatal("NULL parameter to fake_open_memstream");
|
2010-03-29 01:07:08 +02:00
|
|
|
out = tmpfile();
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!out))
|
2010-03-29 01:07:08 +02:00
|
|
|
return NULL;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 03:14:46 +01:00
|
|
|
static inline int fake_open_memstream_update_buffer(FILE *fp, uchar **buf, size_t *length)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
|
|
|
|
long original_pos = ftell(fp);
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(fseek(fp, 0, SEEK_END)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
*length = ftell(fp);
|
|
|
|
|
rewind(fp);
|
|
|
|
|
*buf = (uchar *)malloc(*length);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!*buf))
|
|
|
|
|
return -1;
|
|
|
|
|
if (unlikely(fread(*buf, *length, 1, fp) != 1))
|
|
|
|
|
return -1;
|
|
|
|
|
if (unlikely(fseek(fp, original_pos, SEEK_SET)))
|
|
|
|
|
return -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
***** COMPRESSION FUNCTIONS *****
|
|
|
|
|
|
|
|
|
|
ZPAQ, BZIP, GZIP, LZMA, LZO
|
|
|
|
|
|
|
|
|
|
try to compress a buffer. If compression fails for whatever reason then
|
|
|
|
|
leave uncompressed. Return the compression type in c_type and resulting
|
|
|
|
|
length in c_len
|
|
|
|
|
*/
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
static int zpaq_compress_buf(struct compress_thread *cthread, long thread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *c_buf = NULL;
|
2010-11-03 03:14:46 +01:00
|
|
|
size_t dlen = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
FILE *in, *out;
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
if (!lzo_compresses(cthread->s_buf, cthread->s_len))
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
in = fmemopen(cthread->s_buf, cthread->s_len, "r");
|
2010-12-11 03:19:34 +01:00
|
|
|
if (unlikely(!in)) {
|
|
|
|
|
print_maxverbose("Failed to fmemopen in zpaq_compress_buf\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2010-11-12 15:26:09 +01:00
|
|
|
out = open_memstream((char **)&c_buf, &dlen);
|
2010-12-11 03:19:34 +01:00
|
|
|
if (unlikely(!out)) {
|
|
|
|
|
fclose(in);
|
|
|
|
|
print_maxverbose("Failed to open_memstream in zpaq_compress_buf\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
zpipe_compress(in, out, control.msgout, cthread->s_len,
|
|
|
|
|
(int)(SHOW_PROGRESS), thread);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
if (unlikely(memstream_update_buffer(out, &c_buf, &dlen)))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Failed to memstream_update_buffer in zpaq_compress_buf");
|
|
|
|
|
|
|
|
|
|
fclose(in);
|
|
|
|
|
fclose(out);
|
|
|
|
|
|
2010-12-15 12:54:15 +01:00
|
|
|
if (unlikely((i64)dlen >= cthread->c_len)) {
|
2010-11-10 10:56:17 +01:00
|
|
|
print_maxverbose("Incompressible block\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_len = dlen;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cthread->s_buf);
|
|
|
|
|
cthread->s_buf = c_buf;
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_type = CTYPE_ZPAQ;
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
static int bzip2_compress_buf(struct compress_thread *cthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-10 10:56:17 +01:00
|
|
|
u32 dlen = cthread->s_len;
|
2010-12-15 12:54:15 +01:00
|
|
|
int bzip2_ret;
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
if (!lzo_compresses(cthread->s_buf, cthread->s_len))
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
c_buf = malloc(dlen);
|
2010-12-11 03:19:34 +01:00
|
|
|
if (!c_buf) {
|
|
|
|
|
print_maxverbose("Unable to allocate c_buf\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2010-11-12 15:26:09 +01:00
|
|
|
|
2010-12-15 12:54:15 +01:00
|
|
|
bzip2_ret = BZ2_bzBuffToBuffCompress((char *)c_buf, &dlen,
|
2010-11-10 10:56:17 +01:00
|
|
|
(char *)cthread->s_buf, cthread->s_len,
|
2010-12-15 12:54:15 +01:00
|
|
|
control.compression_level, 0, control.compression_level * 10);
|
|
|
|
|
|
|
|
|
|
/* if compressed data is bigger then original data leave as
|
|
|
|
|
* CTYPE_NONE */
|
|
|
|
|
|
|
|
|
|
if (bzip2_ret == BZ_OUTBUFF_FULL) {
|
|
|
|
|
print_maxverbose("Incompressible block\n");
|
|
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
|
|
|
|
free(c_buf);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(bzip2_ret != BZ_OK)) {
|
|
|
|
|
free(c_buf);
|
|
|
|
|
print_maxverbose("BZ2 compress failed\n");
|
|
|
|
|
return -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
if (unlikely(dlen >= cthread->c_len)) {
|
2010-11-10 10:56:17 +01:00
|
|
|
print_maxverbose("Incompressible block\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_len = dlen;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cthread->s_buf);
|
|
|
|
|
cthread->s_buf = c_buf;
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_type = CTYPE_BZIP2;
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
static int gzip_compress_buf(struct compress_thread *cthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-10 10:56:17 +01:00
|
|
|
unsigned long dlen = cthread->s_len;
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *c_buf;
|
2010-12-15 12:54:15 +01:00
|
|
|
int gzip_ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
c_buf = malloc(dlen);
|
2010-12-11 03:19:34 +01:00
|
|
|
if (!c_buf) {
|
|
|
|
|
print_maxverbose("Unable to allocate c_buf\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2010-11-12 15:26:09 +01:00
|
|
|
|
2010-12-15 12:54:15 +01:00
|
|
|
gzip_ret = compress2(c_buf, &dlen, cthread->s_buf, cthread->s_len,
|
|
|
|
|
control.compression_level);
|
|
|
|
|
|
|
|
|
|
/* if compressed data is bigger then original data leave as
|
|
|
|
|
* CTYPE_NONE */
|
|
|
|
|
|
|
|
|
|
if (gzip_ret == Z_BUF_ERROR) {
|
|
|
|
|
print_maxverbose("Incompressible block\n");
|
|
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
|
|
|
|
free(c_buf);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(gzip_ret != Z_OK)) {
|
|
|
|
|
free(c_buf);
|
|
|
|
|
print_maxverbose("compress2 failed\n");
|
|
|
|
|
return -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-15 12:54:15 +01:00
|
|
|
if (unlikely((i64)dlen >= cthread->c_len)) {
|
2010-11-10 10:56:17 +01:00
|
|
|
print_maxverbose("Incompressible block\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_len = dlen;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cthread->s_buf);
|
|
|
|
|
cthread->s_buf = c_buf;
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_type = CTYPE_GZIP;
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
static int lzma_compress_buf(struct compress_thread *cthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-12-11 03:19:34 +01:00
|
|
|
int lzma_level, lzma_ret;
|
2010-10-31 05:09:05 +01:00
|
|
|
size_t prop_size = 5; /* return value for lzma_properties */
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
size_t dlen;
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
if (!lzo_compresses(cthread->s_buf, cthread->s_len))
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* only 7 levels with lzma, scale them */
|
|
|
|
|
lzma_level = control.compression_level * 7 / 9 ? : 1;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
print_verbose("Starting lzma back end compression thread...\n");
|
|
|
|
|
retry:
|
2010-11-10 10:56:17 +01:00
|
|
|
dlen = cthread->s_len;
|
2010-11-12 15:26:09 +01:00
|
|
|
c_buf = malloc(dlen);
|
|
|
|
|
if (!c_buf)
|
2010-12-11 03:19:34 +01:00
|
|
|
return -1;
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
/* with LZMA SDK 4.63, we pass compression level and threads only
|
|
|
|
|
* and receive properties in control->lzma_properties */
|
|
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
lzma_ret = LzmaCompress(c_buf, &dlen, cthread->s_buf,
|
2010-12-08 10:53:26 +01:00
|
|
|
(size_t)cthread->s_len, control.lzma_properties, &prop_size,
|
2010-12-11 03:19:34 +01:00
|
|
|
lzma_level,
|
2010-12-08 10:53:26 +01:00
|
|
|
0, /* dict size. set default, choose by level */
|
|
|
|
|
-1, -1, -1, -1, /* lc, lp, pb, fb */
|
|
|
|
|
control.threads);
|
2010-03-29 01:07:08 +02:00
|
|
|
if (lzma_ret != SZ_OK) {
|
|
|
|
|
switch (lzma_ret) {
|
|
|
|
|
case SZ_ERROR_MEM:
|
|
|
|
|
break;
|
|
|
|
|
case SZ_ERROR_PARAM:
|
2010-12-03 23:04:38 +01:00
|
|
|
print_err("LZMA Parameter ERROR: %d. This should not happen.\n", SZ_ERROR_PARAM);
|
2010-03-29 01:07:08 +02:00
|
|
|
break;
|
|
|
|
|
case SZ_ERROR_OUTPUT_EOF:
|
2010-12-03 23:04:38 +01:00
|
|
|
print_maxverbose("Harmless LZMA Output Buffer Overflow error: %d. Incompressible block.\n", SZ_ERROR_OUTPUT_EOF);
|
2010-03-29 01:07:08 +02:00
|
|
|
break;
|
|
|
|
|
case SZ_ERROR_THREAD:
|
2010-12-03 23:04:38 +01:00
|
|
|
print_err("LZMA Multi Thread ERROR: %d. This should not happen.\n", SZ_ERROR_THREAD);
|
2010-03-29 01:07:08 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Unidentified LZMA ERROR: %d. This should not happen.\n", lzma_ret);
|
2010-03-29 01:07:08 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* can pass -1 if not compressible! Thanks Lasse Collin */
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-12-04 11:36:51 +01:00
|
|
|
if (lzma_ret == SZ_ERROR_MEM) {
|
2010-12-11 03:19:34 +01:00
|
|
|
if (lzma_level > 1) {
|
|
|
|
|
lzma_level--;
|
|
|
|
|
print_verbose("LZMA Warning: %d. Can't allocate enough RAM for compression window, trying smaller.\n", SZ_ERROR_MEM);
|
|
|
|
|
goto retry;
|
|
|
|
|
}
|
2010-12-04 11:36:51 +01:00
|
|
|
/* lzma compress can be fragile on 32 bit. If it fails,
|
|
|
|
|
* fall back to bzip2 compression so the block doesn't
|
|
|
|
|
* remain uncompressed */
|
2010-12-11 03:19:34 +01:00
|
|
|
print_verbose("Unable to allocate enough RAM for any sized compression window, falling back to bzip2 compression.\n");
|
|
|
|
|
return bzip2_compress_buf(cthread);
|
2010-12-12 07:40:58 +01:00
|
|
|
} else if (lzma_ret == SZ_ERROR_OUTPUT_EOF)
|
|
|
|
|
return 0;
|
2010-12-11 03:19:34 +01:00
|
|
|
return -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
2010-12-11 03:19:34 +01:00
|
|
|
|
|
|
|
|
if (unlikely((i64)dlen >= cthread->c_len)) {
|
2010-03-29 01:07:08 +02:00
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
2010-11-10 10:56:17 +01:00
|
|
|
print_maxverbose("Incompressible block\n");
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_len = dlen;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cthread->s_buf);
|
|
|
|
|
cthread->s_buf = c_buf;
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_type = CTYPE_LZMA;
|
2010-12-11 03:19:34 +01:00
|
|
|
return 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
static int lzo_compress_buf(struct compress_thread *cthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-10 10:56:17 +01:00
|
|
|
lzo_uint in_len = cthread->s_len;
|
2010-11-12 15:26:09 +01:00
|
|
|
lzo_uint dlen = in_len + in_len / 16 + 64 + 3;
|
2010-03-29 01:07:08 +02:00
|
|
|
lzo_int return_var; /* lzo1x_1_compress does not return anything but LZO_OK */
|
2010-10-31 05:09:05 +01:00
|
|
|
lzo_bytep wrkmem;
|
2010-11-12 15:26:09 +01:00
|
|
|
uchar *c_buf;
|
2010-12-11 03:19:34 +01:00
|
|
|
int ret = -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
wrkmem = (lzo_bytep) malloc(LZO1X_1_MEM_COMPRESS);
|
2010-12-11 03:19:34 +01:00
|
|
|
if (unlikely(wrkmem == NULL)) {
|
|
|
|
|
print_maxverbose("Failed to malloc wkmem\n");
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
c_buf = malloc(dlen);
|
|
|
|
|
if (!c_buf)
|
|
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
|
|
return_var = lzo1x_1_compress(cthread->s_buf, in_len, c_buf, &dlen, wrkmem);
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
if (dlen >= in_len){
|
|
|
|
|
/* Incompressible, leave as CTYPE_NONE */
|
2010-11-10 10:56:17 +01:00
|
|
|
print_maxverbose("Incompressible block\n");
|
2010-11-12 15:26:09 +01:00
|
|
|
free(c_buf);
|
2010-03-29 01:07:08 +02:00
|
|
|
goto out_free;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_len = dlen;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cthread->s_buf);
|
|
|
|
|
cthread->s_buf = c_buf;
|
2010-11-10 10:56:17 +01:00
|
|
|
cthread->c_type = CTYPE_LZO;
|
2010-03-29 01:07:08 +02:00
|
|
|
out_free:
|
|
|
|
|
free(wrkmem);
|
2010-12-11 03:19:34 +01:00
|
|
|
return ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
***** DECOMPRESSION FUNCTIONS *****
|
|
|
|
|
|
|
|
|
|
ZPAQ, BZIP, GZIP, LZMA, LZO
|
|
|
|
|
|
|
|
|
|
try to decompress a buffer. Return 0 on success and -1 on failure.
|
|
|
|
|
*/
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static int zpaq_decompress_buf(struct uncomp_thread *ucthread, long thread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-03 03:14:46 +01:00
|
|
|
uchar *c_buf = NULL;
|
|
|
|
|
size_t dlen = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
FILE *in, *out;
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
in = fmemopen(ucthread->s_buf, ucthread->u_len, "r");
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!in)) {
|
|
|
|
|
print_err("Failed to fmemopen in zpaq_decompress_buf\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
out = open_memstream((char **)&c_buf, &dlen);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!out)) {
|
|
|
|
|
print_err("Failed to open_memstream in zpaq_decompress_buf\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
zpipe_decompress(in, out, control.msgout, ucthread->u_len, (int)(SHOW_PROGRESS), thread);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(memstream_update_buffer(out, &c_buf, &dlen)))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Failed to memstream_update_buffer in zpaq_decompress_buf");
|
|
|
|
|
|
|
|
|
|
fclose(in);
|
|
|
|
|
fclose(out);
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely((i64)dlen != ucthread->u_len)) {
|
|
|
|
|
print_err("Inconsistent length after decompression. Got %lld bytes, expected %lld\n", (i64)dlen, ucthread->u_len);
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-11 04:25:07 +01:00
|
|
|
free(ucthread->s_buf);
|
|
|
|
|
ucthread->s_buf = c_buf;
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static int bzip2_decompress_buf(struct uncomp_thread *ucthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-16 11:25:32 +01:00
|
|
|
u32 dlen = ucthread->u_len;
|
2010-12-11 04:25:07 +01:00
|
|
|
int ret = 0, bzerr;
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
c_buf = ucthread->s_buf;
|
|
|
|
|
ucthread->s_buf = malloc(dlen);
|
|
|
|
|
if (unlikely(!ucthread->s_buf)) {
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Failed to allocate %d bytes for decompression\n", dlen);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
bzerr = BZ2_bzBuffToBuffDecompress((char*)ucthread->s_buf, &dlen, (char*)c_buf, ucthread->c_len, 0, 0);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(bzerr != BZ_OK)) {
|
|
|
|
|
print_err("Failed to decompress buffer - bzerr=%d\n", bzerr);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out_free;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(dlen != ucthread->u_len)) {
|
|
|
|
|
print_err("Inconsistent length after decompression. Got %d bytes, expected %lld\n", dlen, ucthread->u_len);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 04:25:07 +01:00
|
|
|
out_free:
|
2010-03-29 01:07:08 +02:00
|
|
|
free(c_buf);
|
2010-12-11 04:25:07 +01:00
|
|
|
out:
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
ucthread->s_buf = c_buf;
|
|
|
|
|
return ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static int gzip_decompress_buf(struct uncomp_thread *ucthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-16 11:25:32 +01:00
|
|
|
unsigned long dlen = ucthread->u_len;
|
2010-12-11 04:25:07 +01:00
|
|
|
int ret = 0, gzerr;
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
c_buf = ucthread->s_buf;
|
|
|
|
|
ucthread->s_buf = malloc(dlen);
|
|
|
|
|
if (unlikely(!ucthread->s_buf)) {
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Failed to allocate %ld bytes for decompression\n", dlen);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
gzerr = uncompress(ucthread->s_buf, &dlen, c_buf, ucthread->c_len);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(gzerr != Z_OK)) {
|
|
|
|
|
print_err("Failed to decompress buffer - bzerr=%d\n", gzerr);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out_free;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely((i64)dlen != ucthread->u_len)) {
|
|
|
|
|
print_err("Inconsistent length after decompression. Got %ld bytes, expected %lld\n", dlen, ucthread->u_len);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 04:25:07 +01:00
|
|
|
out_free:
|
2010-03-29 01:07:08 +02:00
|
|
|
free(c_buf);
|
2010-12-11 04:25:07 +01:00
|
|
|
out:
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
ucthread->s_buf = c_buf;
|
|
|
|
|
return ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static int lzma_decompress_buf(struct uncomp_thread *ucthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-16 11:25:32 +01:00
|
|
|
size_t dlen = (size_t)ucthread->u_len;
|
2010-12-11 04:25:07 +01:00
|
|
|
int ret = 0, lzmaerr;
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
c_buf = ucthread->s_buf;
|
|
|
|
|
ucthread->s_buf = malloc(dlen);
|
|
|
|
|
if (unlikely(!ucthread->s_buf)) {
|
2010-11-05 13:02:58 +01:00
|
|
|
print_err("Failed to allocate %lldd bytes for decompression\n", (i64)dlen);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* With LZMA SDK 4.63 we pass control.lzma_properties
|
|
|
|
|
* which is needed for proper uncompress */
|
2010-11-16 11:25:32 +01:00
|
|
|
lzmaerr = LzmaUncompress(ucthread->s_buf, &dlen, c_buf, (SizeT *)&ucthread->c_len, control.lzma_properties, 5);
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(lzmaerr)) {
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Failed to decompress buffer - lzmaerr=%d\n", lzmaerr);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out_free;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely((i64)dlen != ucthread->u_len)) {
|
|
|
|
|
print_err("Inconsistent length after decompression. Got %lld bytes, expected %lld\n", (i64)dlen, ucthread->u_len);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 04:25:07 +01:00
|
|
|
out_free:
|
2010-03-29 01:07:08 +02:00
|
|
|
free(c_buf);
|
2010-12-11 04:25:07 +01:00
|
|
|
out:
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
ucthread->s_buf = c_buf;
|
|
|
|
|
return ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static int lzo_decompress_buf(struct uncomp_thread *ucthread)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-16 11:25:32 +01:00
|
|
|
lzo_uint dlen = ucthread->u_len;
|
2010-12-11 04:25:07 +01:00
|
|
|
int ret = 0, lzerr;
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *c_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
c_buf = ucthread->s_buf;
|
|
|
|
|
ucthread->s_buf = malloc(dlen);
|
|
|
|
|
if (unlikely(!ucthread->s_buf)) {
|
2010-11-05 13:02:58 +01:00
|
|
|
print_err("Failed to allocate %lu bytes for decompression\n", (unsigned long)dlen);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
lzerr = lzo1x_decompress((uchar*)c_buf, ucthread->c_len, (uchar*)ucthread->s_buf, &dlen,NULL);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(lzerr != LZO_E_OK)) {
|
|
|
|
|
print_err("Failed to decompress buffer - lzerr=%d\n", lzerr);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
|
|
|
|
goto out_free;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely((i64)dlen != ucthread->u_len)) {
|
|
|
|
|
print_err("Inconsistent length after decompression. Got %lu bytes, expected %lld\n", (unsigned long)dlen, ucthread->u_len);
|
2010-12-11 04:25:07 +01:00
|
|
|
ret = -1;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-11 04:25:07 +01:00
|
|
|
out_free:
|
2010-03-29 01:07:08 +02:00
|
|
|
free(c_buf);
|
2010-12-11 04:25:07 +01:00
|
|
|
out:
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
ucthread->s_buf = c_buf;
|
|
|
|
|
return ret;
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* WORK FUNCTIONS */
|
|
|
|
|
|
|
|
|
|
const i64 one_g = 1000 * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
/* This is a custom version of write() which writes in 1GB chunks to avoid
|
|
|
|
|
the overflows at the >= 2GB mark thanks to 32bit fuckage. This should help
|
2010-11-01 02:50:20 +01:00
|
|
|
even on the rare occasion write() fails to write 1GB as well. */
|
2010-03-29 01:07:08 +02:00
|
|
|
ssize_t write_1g(int fd, void *buf, i64 len)
|
|
|
|
|
{
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *offset_buf = buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
i64 total, offset;
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
total = offset = 0;
|
|
|
|
|
while (len > 0) {
|
|
|
|
|
if (len > one_g)
|
|
|
|
|
ret = one_g;
|
|
|
|
|
else
|
|
|
|
|
ret = len;
|
2010-11-01 02:50:20 +01:00
|
|
|
ret = write(fd, offset_buf, (size_t)ret);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret < 0))
|
2010-03-29 01:07:08 +02:00
|
|
|
return ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
offset_buf += ret;
|
|
|
|
|
total += ret;
|
|
|
|
|
}
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ditto for read */
|
|
|
|
|
ssize_t read_1g(int fd, void *buf, i64 len)
|
|
|
|
|
{
|
2010-10-31 05:09:05 +01:00
|
|
|
uchar *offset_buf = buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
i64 total, offset;
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
total = offset = 0;
|
|
|
|
|
while (len > 0) {
|
|
|
|
|
if (len > one_g)
|
|
|
|
|
ret = one_g;
|
|
|
|
|
else
|
|
|
|
|
ret = len;
|
|
|
|
|
ret = read(fd, offset_buf, (size_t)ret);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret < 0))
|
2010-03-29 01:07:08 +02:00
|
|
|
return ret;
|
|
|
|
|
len -= ret;
|
|
|
|
|
offset_buf += ret;
|
|
|
|
|
total += ret;
|
|
|
|
|
}
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write to a file, return 0 on success and -1 on failure */
|
|
|
|
|
static int write_buf(int f, uchar *p, i64 len)
|
|
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
ret = write_1g(f, p, (size_t)len);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret == -1)) {
|
|
|
|
|
print_err("Write of length %lld failed - %s\n", len, strerror(errno));
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret != (ssize_t)len)) {
|
2010-11-05 13:02:58 +01:00
|
|
|
print_err("Partial write!? asked for %lld bytes but got %lld\n", len, (i64)ret);
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write a byte */
|
|
|
|
|
static int write_u8(int f, uchar v)
|
|
|
|
|
{
|
|
|
|
|
return write_buf(f, &v, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write a i64 */
|
|
|
|
|
static int write_i64(int f, i64 v)
|
|
|
|
|
{
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(write_buf(f, (uchar *)&v, 8)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int read_buf(int f, uchar *p, i64 len)
|
|
|
|
|
{
|
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
|
|
ret = read_1g(f, p, (size_t)len);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret == -1)) {
|
|
|
|
|
print_err("Read of length %lld failed - %s\n", len, strerror(errno));
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(ret != (ssize_t)len)) {
|
|
|
|
|
print_err("Partial read!? asked for %lld bytes but got %lld\n", len, (i64)ret);
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int read_u8(int f, uchar *v)
|
|
|
|
|
{
|
|
|
|
|
return read_buf(f, v, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int read_u32(int f, u32 *v)
|
|
|
|
|
{
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_buf(f, (uchar *)v, 4)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int read_i64(int f, i64 *v)
|
|
|
|
|
{
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_buf(f, (uchar *)v, 8)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* seek to a position within a set of streams - return -1 on failure */
|
|
|
|
|
static int seekto(struct stream_info *sinfo, i64 pos)
|
|
|
|
|
{
|
|
|
|
|
i64 spos = pos + sinfo->initial_pos;
|
2010-10-31 05:09:05 +01:00
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(lseek(sinfo->fd, spos, SEEK_SET) != spos)) {
|
|
|
|
|
print_err("Failed to seek to %lld in stream\n", pos);
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static pthread_t *threads;
|
|
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
void prepare_streamout_threads(void)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-10-31 05:09:05 +01:00
|
|
|
int i;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
threads = calloc(sizeof(pthread_t), control.threads);
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(!threads))
|
2010-12-10 13:51:59 +01:00
|
|
|
fatal("Unable to calloc threads in prepare_streamout_threads\n");
|
2010-11-12 15:26:09 +01:00
|
|
|
|
|
|
|
|
cthread = calloc(sizeof(struct compress_thread), control.threads);
|
|
|
|
|
if (unlikely(!cthread))
|
2010-12-10 13:51:59 +01:00
|
|
|
fatal("Unable to calloc cthread in prepare_streamout_threads\n");
|
2010-11-12 15:26:09 +01:00
|
|
|
|
|
|
|
|
for (i = 0; i < control.threads; i++) {
|
|
|
|
|
init_sem(&cthread[i].complete);
|
|
|
|
|
init_sem(&cthread[i].free);
|
|
|
|
|
post_sem(&cthread[i].free);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Threads need to wait on the thread before them before dumping their
|
|
|
|
|
* data. This is done in a circle up to control.threads */
|
|
|
|
|
cthread[0].wait_on = control.threads - 1;
|
|
|
|
|
for (i = 1; i < control.threads; i++)
|
|
|
|
|
cthread[i].wait_on = i - 1;
|
|
|
|
|
|
|
|
|
|
/* Signal thread 0 that it can start */
|
|
|
|
|
if (control.threads > 1)
|
|
|
|
|
post_sem(&cthread[control.threads - 1].complete);
|
2010-12-10 13:51:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void close_streamout_threads(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2010-11-12 15:26:09 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
for (i = 0; i < control.threads; i++) {
|
|
|
|
|
wait_sem(&cthread[i].free);
|
|
|
|
|
destroy_sem(&cthread[i].complete);
|
|
|
|
|
destroy_sem(&cthread[i].free);
|
|
|
|
|
}
|
|
|
|
|
free(cthread);
|
|
|
|
|
free(threads);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* open a set of output streams, compressing with the given
|
|
|
|
|
compression level and algorithm */
|
|
|
|
|
void *open_stream_out(int f, int n, i64 limit, char cbytes)
|
|
|
|
|
{
|
|
|
|
|
struct stream_info *sinfo;
|
|
|
|
|
uchar *testmalloc;
|
|
|
|
|
i64 testsize;
|
2011-02-05 23:00:43 +01:00
|
|
|
int i, testbufs;
|
2010-12-10 13:51:59 +01:00
|
|
|
|
|
|
|
|
sinfo = calloc(sizeof(struct stream_info), 1);
|
|
|
|
|
if (unlikely(!sinfo))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
sinfo->bufsize = limit;
|
|
|
|
|
|
|
|
|
|
sinfo->chunk_bytes = cbytes;
|
2010-03-29 01:07:08 +02:00
|
|
|
sinfo->num_streams = n;
|
|
|
|
|
sinfo->fd = f;
|
|
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
sinfo->s = calloc(sizeof(struct stream), n);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!sinfo->s)) {
|
2010-03-29 01:07:08 +02:00
|
|
|
free(sinfo);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-02 03:50:15 +01:00
|
|
|
/* Find the largest we can make the window based on ability to malloc
|
|
|
|
|
* ram. We need enough for the 2 streams and for the compression
|
2011-02-05 23:00:43 +01:00
|
|
|
* backend at most, being conservative. We don't need any for the
|
|
|
|
|
* backend compression if we won't be doing any.
|
|
|
|
|
*/
|
|
|
|
|
testbufs = n;
|
|
|
|
|
if (!NO_COMPRESS)
|
|
|
|
|
testbufs++;
|
|
|
|
|
|
|
|
|
|
/* Serious limits imposed on 32 bit capabilities */
|
|
|
|
|
if (BITS32)
|
|
|
|
|
limit = MIN(limit, two_gig / testbufs);
|
|
|
|
|
|
2010-11-02 03:50:15 +01:00
|
|
|
retest_malloc:
|
2011-02-05 23:00:43 +01:00
|
|
|
testsize = limit * testbufs;
|
2010-12-03 22:39:52 +01:00
|
|
|
testmalloc = malloc(testsize);
|
2010-11-02 03:50:15 +01:00
|
|
|
if (!testmalloc) {
|
2010-11-19 15:23:08 +01:00
|
|
|
limit = limit / 10 * 9;
|
2010-11-02 03:50:15 +01:00
|
|
|
goto retest_malloc;
|
|
|
|
|
}
|
|
|
|
|
free(testmalloc);
|
2010-12-03 22:39:52 +01:00
|
|
|
print_maxverbose("Succeeded in testing %lld sized malloc for back end compression\n", testsize);
|
2010-11-19 15:23:08 +01:00
|
|
|
|
|
|
|
|
sinfo->bufsize = limit;
|
2010-11-12 15:26:09 +01:00
|
|
|
|
|
|
|
|
/* Make the bufsize no smaller than STREAM_BUFSIZE. Round up the
|
|
|
|
|
* bufsize to fit X threads into it */
|
|
|
|
|
sinfo->bufsize = MIN(sinfo->bufsize,
|
|
|
|
|
MAX((sinfo->bufsize + control.threads - 1) / control.threads, STREAM_BUFSIZE));
|
|
|
|
|
|
|
|
|
|
if (control.threads > 1)
|
|
|
|
|
print_maxverbose("Using %d threads to compress up to %lld bytes each.\n",
|
|
|
|
|
control.threads, sinfo->bufsize);
|
|
|
|
|
else
|
|
|
|
|
print_maxverbose("Using 1 thread to compress up to %lld bytes\n",
|
|
|
|
|
sinfo->bufsize);
|
2010-11-02 03:50:15 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
sinfo->s[i].buf = malloc(sinfo->bufsize);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!sinfo->s[i].buf))
|
2010-11-01 12:55:59 +01:00
|
|
|
fatal("Unable to malloc buffer of size %lld in open_stream_out\n", sinfo->bufsize);
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (void *)sinfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* prepare a set of n streams for reading on file descriptor f */
|
|
|
|
|
void *open_stream_in(int f, int n)
|
|
|
|
|
{
|
2010-10-31 05:09:05 +01:00
|
|
|
struct stream_info *sinfo;
|
2010-12-03 09:35:48 +01:00
|
|
|
int total_threads, i;
|
2010-03-29 01:07:08 +02:00
|
|
|
i64 header_length;
|
|
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
sinfo = calloc(sizeof(struct stream_info), 1);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!sinfo))
|
2010-03-29 01:07:08 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
total_threads = control.threads * 2;
|
2010-12-10 13:51:59 +01:00
|
|
|
threads = calloc(sizeof(pthread_t), total_threads);
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(!threads))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2010-12-03 09:35:48 +01:00
|
|
|
ucthread = calloc(sizeof(struct uncomp_thread), total_threads);
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(!ucthread))
|
2010-12-03 22:39:52 +01:00
|
|
|
fatal("Unable to calloc cthread in open_stream_in\n");
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2010-12-03 09:35:48 +01:00
|
|
|
for (i = 0; i < total_threads; i++) {
|
2010-11-16 11:25:32 +01:00
|
|
|
init_sem(&ucthread[i].complete);
|
|
|
|
|
init_sem(&ucthread[i].ready);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
sinfo->num_streams = n;
|
|
|
|
|
sinfo->fd = f;
|
|
|
|
|
sinfo->initial_pos = lseek(f, 0, SEEK_CUR);
|
|
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
sinfo->s = calloc(sizeof(struct stream), n);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!sinfo->s)) {
|
2010-03-29 01:07:08 +02:00
|
|
|
free(sinfo);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
uchar c;
|
|
|
|
|
i64 v1, v2;
|
|
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
sinfo->s[i].base_thread = control.threads * i;
|
2010-12-03 09:35:48 +01:00
|
|
|
sinfo->s[i].uthread_no = sinfo->s[i].base_thread;
|
|
|
|
|
sinfo->s[i].unext_thread = sinfo->s[i].base_thread;
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
again:
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_u8(f, &c)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
|
|
|
|
|
/* Compatibility crap for versions < 0.40 */
|
|
|
|
|
if (control.major_version == 0 && control.minor_version < 4) {
|
|
|
|
|
u32 v132, v232, last_head32;
|
|
|
|
|
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(read_u32(f, &v132)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(read_u32(f, &v232)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
2010-11-05 04:52:14 +01:00
|
|
|
if ((read_u32(f, &last_head32)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
|
|
|
|
|
v1 = v132;
|
|
|
|
|
v2 = v232;
|
|
|
|
|
sinfo->s[i].last_head = last_head32;
|
|
|
|
|
header_length = 13;
|
|
|
|
|
} else {
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_i64(f, &v1)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_i64(f, &v2)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_i64(f, &sinfo->s[i].last_head)))
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
header_length = 25;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(c == CTYPE_NONE && v1 == 0 && v2 == 0 && sinfo->s[i].last_head == 0 && i == 0)) {
|
|
|
|
|
print_err("Enabling stream close workaround\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
sinfo->initial_pos += header_length;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sinfo->total_read += header_length;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(c != CTYPE_NONE)) {
|
|
|
|
|
print_err("Unexpected initial tag %d in streams\n", c);
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
}
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(v1)) {
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Unexpected initial c_len %lld in streams %lld\n", v1, v2);
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
}
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(v2)) {
|
2010-11-05 02:16:43 +01:00
|
|
|
print_err("Unexpected initial u_len %lld in streams\n", v2);
|
2010-03-29 01:07:08 +02:00
|
|
|
goto failed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (void *)sinfo;
|
|
|
|
|
|
|
|
|
|
failed:
|
|
|
|
|
free(sinfo->s);
|
|
|
|
|
free(sinfo);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
/* Enter with s_buf allocated,s_buf points to the compressed data after the
|
|
|
|
|
* backend compression and is then freed here */
|
2010-11-16 11:25:32 +01:00
|
|
|
static void *compthread(void *t)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-10 10:56:17 +01:00
|
|
|
long i = (long)t;
|
2010-11-12 15:26:09 +01:00
|
|
|
struct compress_thread *cti = &cthread[i];
|
2010-12-10 13:51:59 +01:00
|
|
|
struct stream_info *ctis = cti->sinfo;
|
2010-12-11 03:19:34 +01:00
|
|
|
int waited = 0, ret = 0;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-24 10:12:19 +01:00
|
|
|
if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val) == -1))
|
|
|
|
|
print_err("Warning, unable to set nice value on thread\n");
|
|
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
cti->c_type = CTYPE_NONE;
|
|
|
|
|
cti->c_len = cti->s_len;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-07 22:27:22 +01:00
|
|
|
/* Flushing writes to disk frees up any dirty ram, improving chances
|
2010-12-18 00:02:19 +01:00
|
|
|
* of succeeding in allocating more ram */
|
|
|
|
|
fsync(ctis->fd);
|
2010-12-11 03:19:34 +01:00
|
|
|
retry:
|
2010-11-12 15:26:09 +01:00
|
|
|
if (!NO_COMPRESS && cti->c_len) {
|
2010-11-01 11:37:55 +01:00
|
|
|
if (LZMA_COMPRESS)
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = lzma_compress_buf(cti);
|
2010-11-01 01:18:58 +01:00
|
|
|
else if (LZO_COMPRESS)
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = lzo_compress_buf(cti);
|
2010-11-01 01:18:58 +01:00
|
|
|
else if (BZIP2_COMPRESS)
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = bzip2_compress_buf(cti);
|
2010-11-01 01:18:58 +01:00
|
|
|
else if (ZLIB_COMPRESS)
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = gzip_compress_buf(cti);
|
2010-11-01 01:18:58 +01:00
|
|
|
else if (ZPAQ_COMPRESS)
|
2010-12-11 03:19:34 +01:00
|
|
|
ret = zpaq_compress_buf(cti, i);
|
2010-03-29 01:07:08 +02:00
|
|
|
else fatal("Dunno wtf compression to use!\n");
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
/* If compression fails for whatever reason multithreaded, then wait
|
|
|
|
|
* for the previous thread to finish, serialising the work to decrease
|
|
|
|
|
* the memory requirements, increasing the chance of success */
|
|
|
|
|
if (ret){
|
|
|
|
|
if (waited)
|
|
|
|
|
fatal("Failed to compress in compthread\n");
|
|
|
|
|
print_maxverbose("Unable to compress in parallel, waiting for previous thread to complete before trying again\n");
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-12 00:39:52 +01:00
|
|
|
if (control.threads > 1 && !waited)
|
2010-11-12 15:26:09 +01:00
|
|
|
wait_sem(&cthread[cti->wait_on].complete);
|
2010-12-11 03:19:34 +01:00
|
|
|
waited = 1;
|
|
|
|
|
if (ret)
|
|
|
|
|
goto retry;
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
if (!ctis->chunks++) {
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
/* Write chunk bytes of this block */
|
|
|
|
|
write_u8(ctis->fd, ctis->chunk_bytes);
|
|
|
|
|
|
|
|
|
|
/* First chunk of this stream, write headers */
|
|
|
|
|
ctis->initial_pos = lseek(ctis->fd, 0, SEEK_CUR);
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < ctis->num_streams; j++) {
|
|
|
|
|
ctis->s[j].last_head = ctis->cur_pos + 17;
|
|
|
|
|
write_u8(ctis->fd, CTYPE_NONE);
|
|
|
|
|
write_i64(ctis->fd, 0);
|
|
|
|
|
write_i64(ctis->fd, 0);
|
|
|
|
|
write_i64(ctis->fd, 0);
|
|
|
|
|
ctis->cur_pos += 25;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(seekto(ctis, ctis->s[cti->stream].last_head)))
|
2010-11-12 15:26:09 +01:00
|
|
|
fatal("Failed to seekto in compthread %d\n", i);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
if (unlikely(write_i64(ctis->fd, ctis->cur_pos)))
|
2010-11-12 15:26:09 +01:00
|
|
|
fatal("Failed to write_i64 in compthread %d\n", i);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
ctis->s[cti->stream].last_head = ctis->cur_pos + 17;
|
|
|
|
|
if (unlikely(seekto(ctis, ctis->cur_pos)))
|
2010-11-12 15:26:09 +01:00
|
|
|
fatal("Failed to seekto cur_pos in compthread %d\n", i);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
print_maxverbose("Thread %ld writing %lld compressed bytes from stream %d\n", i, cti->c_len, cti->stream);
|
|
|
|
|
if (unlikely(write_u8(ctis->fd, cti->c_type) ||
|
|
|
|
|
write_i64(ctis->fd, cti->c_len) ||
|
|
|
|
|
write_i64(ctis->fd, cti->s_len) ||
|
|
|
|
|
write_i64(ctis->fd, 0))) {
|
2010-11-12 15:26:09 +01:00
|
|
|
fatal("Failed write in compthread %d\n", i);
|
2010-11-10 10:56:17 +01:00
|
|
|
}
|
2010-12-10 13:51:59 +01:00
|
|
|
ctis->cur_pos += 25;
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
if (unlikely(write_buf(ctis->fd, cti->s_buf, cti->c_len)))
|
2010-11-12 15:26:09 +01:00
|
|
|
fatal("Failed to write_buf in compthread %d\n", i);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
ctis->cur_pos += cti->c_len;
|
2010-11-12 15:26:09 +01:00
|
|
|
free(cti->s_buf);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
post_sem(&cti->complete);
|
|
|
|
|
post_sem(&cti->free);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
static void clear_buffer(struct stream_info *sinfo, int stream, int newbuf)
|
2010-11-12 15:26:09 +01:00
|
|
|
{
|
2010-12-10 13:51:59 +01:00
|
|
|
static long i = 0;
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
/* Make sure this thread doesn't already exist */
|
|
|
|
|
wait_sem(&cthread[i].free);
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
cthread[i].sinfo = sinfo;
|
|
|
|
|
cthread[i].stream = stream;
|
|
|
|
|
cthread[i].s_buf = sinfo->s[stream].buf;
|
|
|
|
|
cthread[i].s_len = sinfo->s[stream].buflen;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
print_maxverbose("Starting thread %ld to compress %lld bytes from stream %d\n",
|
2010-11-16 11:25:32 +01:00
|
|
|
i, cthread[i].s_len, stream);
|
2010-11-12 15:26:09 +01:00
|
|
|
create_pthread(&threads[i], NULL, compthread, (void *)i);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
if (newbuf) {
|
|
|
|
|
/* The stream buffer has been given to the thread, allocate a new one */
|
|
|
|
|
sinfo->s[stream].buf = malloc(sinfo->bufsize);
|
|
|
|
|
if (unlikely(!sinfo->s[stream].buf))
|
|
|
|
|
fatal("Unable to malloc buffer of size %lld in flush_buffer\n", sinfo->bufsize);
|
|
|
|
|
sinfo->s[stream].buflen = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (++i == control.threads)
|
|
|
|
|
i = 0;
|
|
|
|
|
}
|
2010-11-10 10:56:17 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
/* flush out any data in a stream buffer */
|
|
|
|
|
void flush_buffer(struct stream_info *sinfo, int stream)
|
|
|
|
|
{
|
|
|
|
|
clear_buffer(sinfo, stream, 1);
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
static void *ucompthread(void *t)
|
|
|
|
|
{
|
|
|
|
|
long i = (long)t;
|
|
|
|
|
struct uncomp_thread *uci = &ucthread[i];
|
2010-12-11 03:19:34 +01:00
|
|
|
int waited = 0, ret = 0;
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2010-11-24 10:12:19 +01:00
|
|
|
if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val) == -1))
|
|
|
|
|
print_err("Warning, unable to set nice value on thread\n");
|
|
|
|
|
|
2010-12-11 03:19:34 +01:00
|
|
|
retry:
|
2010-11-16 11:25:32 +01:00
|
|
|
if (uci->c_type != CTYPE_NONE) {
|
2010-12-11 03:19:34 +01:00
|
|
|
switch (uci->c_type) {
|
|
|
|
|
case CTYPE_LZMA:
|
|
|
|
|
ret = lzma_decompress_buf(uci);
|
|
|
|
|
break;
|
|
|
|
|
case CTYPE_LZO:
|
|
|
|
|
ret = lzo_decompress_buf(uci);
|
|
|
|
|
break;
|
|
|
|
|
case CTYPE_BZIP2:
|
|
|
|
|
ret = bzip2_decompress_buf(uci);
|
|
|
|
|
break;
|
|
|
|
|
case CTYPE_GZIP:
|
|
|
|
|
ret = gzip_decompress_buf(uci);
|
|
|
|
|
break;
|
|
|
|
|
case CTYPE_ZPAQ:
|
|
|
|
|
ret = zpaq_decompress_buf(uci, i);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fatal("Dunno wtf decompression type to use!\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* As per compression, serialise the decompression if it fails in
|
|
|
|
|
* parallel */
|
|
|
|
|
if (ret) {
|
|
|
|
|
if (waited)
|
|
|
|
|
fatal("Failed to decompress in ucompthread\n");
|
|
|
|
|
print_maxverbose("Unable to decompress in parallel, waiting for previous thread to complete before trying again\n");
|
2011-02-06 01:58:47 +01:00
|
|
|
/* This "ready" tells this thread that we're ready to receive
|
|
|
|
|
* its data. We do not strictly need to wait for this, so it's
|
|
|
|
|
* used when decompression fails due to inadequate memory to
|
|
|
|
|
* try again serialised. */
|
2010-12-12 00:39:52 +01:00
|
|
|
wait_sem(&uci->ready);
|
2011-02-06 01:21:36 +01:00
|
|
|
waited = 1;
|
2010-12-11 03:19:34 +01:00
|
|
|
goto retry;
|
2011-02-06 01:58:47 +01:00
|
|
|
}
|
2010-12-11 03:19:34 +01:00
|
|
|
|
2011-02-06 01:58:47 +01:00
|
|
|
/* This "complete" tells the main thread that this thread has its
|
2010-12-12 00:39:52 +01:00
|
|
|
* decompressed data ready */
|
|
|
|
|
post_sem(&uci->complete);
|
|
|
|
|
|
2011-02-06 01:58:47 +01:00
|
|
|
print_maxverbose("Thread %ld decompressed %lld bytes from stream %d\n", i, uci->u_len, uci->stream);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
static int threads_busy;
|
|
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
/* fill a buffer from a stream - return -1 on failure */
|
2010-12-03 09:35:48 +01:00
|
|
|
static int fill_buffer(struct stream_info *sinfo, int stream)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-16 11:25:32 +01:00
|
|
|
i64 header_length, u_len, c_len, last_head;
|
2011-02-08 01:58:01 +01:00
|
|
|
struct stream *ret_s, *s;
|
2010-11-16 11:25:32 +01:00
|
|
|
uchar c_type, *s_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
ret_s = s = &sinfo->s[stream];
|
|
|
|
|
|
|
|
|
|
fill_different_stream:
|
2010-12-03 09:35:48 +01:00
|
|
|
if (s->buf)
|
|
|
|
|
free(s->buf);
|
2011-02-08 01:58:01 +01:00
|
|
|
if (s->eos) {
|
|
|
|
|
stream ^= 1;
|
|
|
|
|
if (sinfo->s[stream].eos)
|
|
|
|
|
goto out;
|
|
|
|
|
s = &sinfo->s[stream];
|
|
|
|
|
goto fill_different_stream;
|
|
|
|
|
}
|
2011-02-06 01:21:36 +01:00
|
|
|
fill_another:
|
2010-12-03 09:35:48 +01:00
|
|
|
if (unlikely(seekto(sinfo, s->last_head)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_u8(sinfo->fd, &c_type)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-24 10:12:19 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
/* Compatibility crap for versions < 0.4 */
|
|
|
|
|
if (control.major_version == 0 && control.minor_version < 4) {
|
|
|
|
|
u32 c_len32, u_len32, last_head32;
|
|
|
|
|
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(read_u32(sinfo->fd, &c_len32)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(read_u32(sinfo->fd, &u_len32)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-05 04:52:14 +01:00
|
|
|
if (unlikely(read_u32(sinfo->fd, &last_head32)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
c_len = c_len32;
|
|
|
|
|
u_len = u_len32;
|
2010-11-16 11:25:32 +01:00
|
|
|
last_head = last_head32;
|
2010-03-29 01:07:08 +02:00
|
|
|
header_length = 13;
|
|
|
|
|
} else {
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_i64(sinfo->fd, &c_len)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(read_i64(sinfo->fd, &u_len)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(read_i64(sinfo->fd, &last_head)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
header_length = 25;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sinfo->total_read += header_length;
|
|
|
|
|
|
2011-02-07 22:27:22 +01:00
|
|
|
fsync(control.fd_out);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
|
|
|
|
s_buf = malloc(u_len);
|
|
|
|
|
if (unlikely(u_len && !s_buf))
|
2010-11-01 14:08:35 +01:00
|
|
|
fatal("Unable to malloc buffer of size %lld in fill_buffer\n", u_len);
|
2010-11-12 15:26:09 +01:00
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
if (unlikely(read_buf(sinfo->fd, s_buf, c_len)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
sinfo->total_read += c_len;
|
|
|
|
|
|
2010-12-03 09:35:48 +01:00
|
|
|
ucthread[s->uthread_no].s_buf = s_buf;
|
|
|
|
|
ucthread[s->uthread_no].c_len = c_len;
|
|
|
|
|
ucthread[s->uthread_no].u_len = u_len;
|
|
|
|
|
ucthread[s->uthread_no].c_type = c_type;
|
|
|
|
|
ucthread[s->uthread_no].stream = stream;
|
|
|
|
|
s->last_head = last_head;
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2011-02-07 22:55:36 +01:00
|
|
|
/* List this thread as busy */
|
|
|
|
|
ucthread[s->uthread_no].busy = 1;
|
2011-02-08 01:58:01 +01:00
|
|
|
threads_busy++;
|
2010-11-16 11:25:32 +01:00
|
|
|
print_maxverbose("Starting thread %ld to decompress %lld bytes from stream %d\n",
|
2010-12-03 09:35:48 +01:00
|
|
|
s->uthread_no, c_len, stream);
|
|
|
|
|
create_pthread(&threads[s->uthread_no], NULL, ucompthread, (void *)s->uthread_no);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
if (++s->uthread_no == s->base_thread + control.threads)
|
2010-12-03 09:35:48 +01:00
|
|
|
s->uthread_no = s->base_thread;
|
2011-02-06 01:21:36 +01:00
|
|
|
|
2011-02-07 22:55:36 +01:00
|
|
|
/* Reached the end of this stream, no more data to read in, otherwise
|
|
|
|
|
* see if the next thread is free to grab more data */
|
2011-02-06 01:21:36 +01:00
|
|
|
if (!last_head)
|
|
|
|
|
s->eos = 1;
|
2011-02-08 01:58:01 +01:00
|
|
|
if (s->eos) {
|
|
|
|
|
stream ^= 1;
|
|
|
|
|
if (sinfo->s[stream].eos)
|
|
|
|
|
goto out;
|
|
|
|
|
s = &sinfo->s[stream];
|
|
|
|
|
goto fill_different_stream;
|
|
|
|
|
}
|
|
|
|
|
if (s->uthread_no != s->unext_thread &&
|
|
|
|
|
!ucthread[s->uthread_no].busy && threads_busy < control.threads)
|
2011-02-07 22:55:36 +01:00
|
|
|
goto fill_another;
|
2010-11-16 11:25:32 +01:00
|
|
|
out:
|
2011-02-08 01:58:01 +01:00
|
|
|
s = ret_s;
|
|
|
|
|
|
2011-02-06 01:58:47 +01:00
|
|
|
/* "ready" tells the decompression thread we're ready for its data */
|
2010-12-12 00:39:52 +01:00
|
|
|
post_sem(&ucthread[s->unext_thread].ready);
|
2010-12-18 00:02:19 +01:00
|
|
|
/* This "complete" is the deco thread telling us it's finished
|
2010-12-12 00:39:52 +01:00
|
|
|
* decompressing data */
|
2010-12-03 09:35:48 +01:00
|
|
|
wait_sem(&ucthread[s->unext_thread].complete);
|
2011-02-06 01:58:47 +01:00
|
|
|
print_maxverbose("Taking decompressed data from thread %ld\n", s->unext_thread);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2010-12-03 09:35:48 +01:00
|
|
|
s->buf = ucthread[s->unext_thread].s_buf;
|
|
|
|
|
s->buflen = ucthread[s->unext_thread].u_len;
|
|
|
|
|
s->bufp = 0;
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2011-02-06 01:21:36 +01:00
|
|
|
join_pthread(threads[s->unext_thread], NULL);
|
2011-02-07 22:55:36 +01:00
|
|
|
ucthread[s->unext_thread].busy = 0;
|
2011-02-08 01:58:01 +01:00
|
|
|
threads_busy--;
|
2011-02-06 01:58:47 +01:00
|
|
|
/* As the ready semaphore may or may not have been waited on in
|
|
|
|
|
* ucompthread, we reset it regardless. */
|
|
|
|
|
init_sem(&ucthread[s->unext_thread].ready);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2011-02-08 01:58:01 +01:00
|
|
|
if (++s->unext_thread == s->base_thread + control.threads)
|
2010-12-03 09:35:48 +01:00
|
|
|
s->unext_thread = s->base_thread;
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write some data to a stream. Return -1 on failure */
|
|
|
|
|
int write_stream(void *ss, int stream, uchar *p, i64 len)
|
|
|
|
|
{
|
|
|
|
|
struct stream_info *sinfo = ss;
|
|
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
|
i64 n;
|
|
|
|
|
|
|
|
|
|
n = MIN(sinfo->bufsize - sinfo->s[stream].buflen, len);
|
|
|
|
|
|
2010-11-10 10:56:17 +01:00
|
|
|
memcpy(sinfo->s[stream].buf + sinfo->s[stream].buflen, p, n);
|
2010-03-29 01:07:08 +02:00
|
|
|
sinfo->s[stream].buflen += n;
|
|
|
|
|
p += n;
|
|
|
|
|
len -= n;
|
|
|
|
|
|
2010-11-12 15:26:09 +01:00
|
|
|
/* Flush the buffer every sinfo->bufsize into one thread */
|
|
|
|
|
if (sinfo->s[stream].buflen == sinfo->bufsize)
|
|
|
|
|
flush_buffer(sinfo, stream);
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read some data from a stream. Return number of bytes read, or -1
|
|
|
|
|
on failure */
|
|
|
|
|
i64 read_stream(void *ss, int stream, uchar *p, i64 len)
|
|
|
|
|
{
|
|
|
|
|
struct stream_info *sinfo = ss;
|
|
|
|
|
i64 ret = 0;
|
|
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
|
i64 n;
|
|
|
|
|
|
|
|
|
|
n = MIN(sinfo->s[stream].buflen-sinfo->s[stream].bufp, len);
|
|
|
|
|
|
|
|
|
|
if (n > 0) {
|
2010-11-10 10:56:17 +01:00
|
|
|
memcpy(p, sinfo->s[stream].buf + sinfo->s[stream].bufp, n);
|
2010-03-29 01:07:08 +02:00
|
|
|
sinfo->s[stream].bufp += n;
|
|
|
|
|
p += n;
|
|
|
|
|
len -= n;
|
|
|
|
|
ret += n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len && sinfo->s[stream].bufp == sinfo->s[stream].buflen) {
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(fill_buffer(sinfo, stream)))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
|
|
|
|
if (sinfo->s[stream].bufp == sinfo->s[stream].buflen)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* flush and close down a stream. return -1 on failure */
|
|
|
|
|
int close_stream_out(void *ss)
|
|
|
|
|
{
|
|
|
|
|
struct stream_info *sinfo = ss;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sinfo->num_streams; i++) {
|
2010-11-12 15:26:09 +01:00
|
|
|
if (sinfo->s[i].buflen)
|
2010-12-10 13:51:59 +01:00
|
|
|
clear_buffer(sinfo, i, 0);
|
2010-03-29 01:07:08 +02:00
|
|
|
}
|
2010-11-12 15:26:09 +01:00
|
|
|
|
2010-12-10 13:51:59 +01:00
|
|
|
#if 0
|
|
|
|
|
/* These cannot be freed because their values are read after the next
|
|
|
|
|
* stream has started so they're not properly freed and just dropped on
|
|
|
|
|
* program exit! FIXME */
|
2010-03-29 01:07:08 +02:00
|
|
|
free(sinfo->s);
|
|
|
|
|
free(sinfo);
|
2010-12-10 13:51:59 +01:00
|
|
|
#endif
|
2010-03-29 01:07:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* close down an input stream */
|
|
|
|
|
int close_stream_in(void *ss)
|
|
|
|
|
{
|
|
|
|
|
struct stream_info *sinfo = ss;
|
|
|
|
|
int i;
|
|
|
|
|
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(lseek(sinfo->fd, sinfo->initial_pos + sinfo->total_read,
|
|
|
|
|
SEEK_SET) != sinfo->initial_pos + sinfo->total_read))
|
2010-03-29 01:07:08 +02:00
|
|
|
return -1;
|
2010-11-12 15:26:09 +01:00
|
|
|
for (i = 0; i < sinfo->num_streams; i++)
|
|
|
|
|
free(sinfo->s[i].buf);
|
2010-03-29 01:07:08 +02:00
|
|
|
|
2010-12-12 00:39:52 +01:00
|
|
|
for (i = 0; i < control.threads + 1; i++) {
|
|
|
|
|
destroy_sem(&ucthread[i].complete);
|
|
|
|
|
destroy_sem(&ucthread[i].ready);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
free(ucthread);
|
|
|
|
|
free(threads);
|
2010-03-29 01:07:08 +02:00
|
|
|
free(sinfo->s);
|
|
|
|
|
free(sinfo);
|
2010-11-16 11:25:32 +01:00
|
|
|
|
2010-03-29 01:07:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* As others are slow and lzo very fast, it is worth doing a quick lzo pass
|
|
|
|
|
to see if there is any compression at all with lzo first. It is unlikely
|
|
|
|
|
that others will be able to compress if lzo is unable to drop a single byte
|
|
|
|
|
so do not compress any block that is incompressible by lzo. */
|
2010-11-10 10:56:17 +01:00
|
|
|
static int lzo_compresses(uchar *s_buf, i64 s_len)
|
2010-03-29 01:07:08 +02:00
|
|
|
{
|
2010-11-10 10:56:17 +01:00
|
|
|
lzo_bytep wrkmem = NULL;
|
|
|
|
|
lzo_uint in_len, test_len = s_len, save_len = s_len;
|
2010-03-29 01:07:08 +02:00
|
|
|
lzo_uint dlen;
|
|
|
|
|
lzo_int return_var; /* lzo1x_1_compress does not return anything but LZO_OK */
|
2010-11-10 10:56:17 +01:00
|
|
|
uchar *c_buf = NULL, *test_buf = s_buf;
|
2010-03-29 01:07:08 +02:00
|
|
|
/* set minimum buffer test size based on the length of the test stream */
|
|
|
|
|
unsigned long buftest_size = (test_len > 5 * STREAM_BUFSIZE ? STREAM_BUFSIZE : STREAM_BUFSIZE / 4096);
|
|
|
|
|
int ret = 0;
|
|
|
|
|
int workcounter = 0; /* count # of passes */
|
|
|
|
|
lzo_uint best_dlen = UINT_MAX; /* save best compression estimate */
|
|
|
|
|
|
|
|
|
|
if (control.threshold > 1)
|
|
|
|
|
return 1;
|
|
|
|
|
wrkmem = (lzo_bytep) malloc(LZO1X_1_MEM_COMPRESS);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(wrkmem == NULL))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Unable to allocate wrkmem in lzo_compresses\n");
|
|
|
|
|
|
|
|
|
|
in_len = MIN(test_len, buftest_size);
|
|
|
|
|
dlen = STREAM_BUFSIZE + STREAM_BUFSIZE / 16 + 64 + 3;
|
|
|
|
|
|
|
|
|
|
c_buf = malloc(dlen);
|
2010-11-05 02:16:43 +01:00
|
|
|
if (unlikely(!c_buf))
|
2010-03-29 01:07:08 +02:00
|
|
|
fatal("Unable to allocate c_buf in lzo_compresses\n");
|
|
|
|
|
|
2010-11-16 11:25:32 +01:00
|
|
|
print_verbose("lzo testing for incompressible data...\n");
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
/* Test progressively larger blocks at a time and as soon as anything
|
|
|
|
|
compressible is found, jump out as a success */
|
|
|
|
|
while (test_len > 0) {
|
|
|
|
|
workcounter++;
|
|
|
|
|
return_var = lzo1x_1_compress(test_buf, in_len, (uchar *)c_buf, &dlen, wrkmem);
|
|
|
|
|
|
|
|
|
|
if (dlen < best_dlen)
|
|
|
|
|
best_dlen = dlen; /* save best value */
|
|
|
|
|
|
|
|
|
|
if ((double) dlen < (double)in_len * control.threshold) {
|
|
|
|
|
ret = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* expand and move buffer */
|
|
|
|
|
test_len -= in_len;
|
|
|
|
|
if (test_len) {
|
|
|
|
|
test_buf += (ptrdiff_t)in_len;
|
|
|
|
|
if (buftest_size < STREAM_BUFSIZE)
|
|
|
|
|
buftest_size <<= 1;
|
|
|
|
|
in_len = MIN(test_len, buftest_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-31 18:53:53 +01:00
|
|
|
if (MAX_VERBOSE)
|
2010-11-01 01:18:58 +01:00
|
|
|
print_output("%s for chunk %ld. Compressed size = %5.2F%% of chunk, %d Passes\n",
|
2010-03-29 01:07:08 +02:00
|
|
|
(ret == 0? "FAILED - below threshold" : "OK"), save_len,
|
|
|
|
|
100 * ((double) best_dlen / (double) in_len), workcounter);
|
2010-10-31 18:53:53 +01:00
|
|
|
else if (VERBOSE)
|
2010-11-12 15:26:09 +01:00
|
|
|
print_output("%s\n", (ret == 0? "FAILED - below threshold" : "OK"));
|
2010-03-29 01:07:08 +02:00
|
|
|
|
|
|
|
|
free(wrkmem);
|
|
|
|
|
free(c_buf);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|