pcloudcc-lneely/pclsync/pdeflate.c

278 lines
9.0 KiB
C

/*
Copyright (c) 2015 Anton Titov.
Copyright (c) 2015 pCloud Ltd. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met: Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer. Redistributions in binary form must reproduce the
above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution. Neither the name of pCloud Ltd nor the
names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud
Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "pdeflate.h"
#include "pmem.h"
#include "pdbg.h"
#define BUFFER_SIZE (4 * 1024)
#define FLAG_DEFLATE 1
#define FLAG_MORE_DATA 2
#define FLAG_STREAM_END 4
struct _psync_deflate_t {
z_stream stream;
unsigned char *flushbuff;
uint32_t flushbufflen;
uint32_t flushbuffoff;
uint32_t bufferstartoff;
uint32_t bufferendoff;
uint32_t lastout;
uint32_t flags;
unsigned char buffer[BUFFER_SIZE];
};
psync_deflate_t *pdeflate_init(int level) {
psync_deflate_t *def;
int ret;
def = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(psync_deflate_t));
memset(&def->stream, 0, sizeof(def->stream));
def->flushbuff = NULL;
def->bufferstartoff = 0;
def->bufferendoff = 0;
if (level == PSYNC_DEFLATE_DECOMPRESS) {
def->flags = 0;
ret = inflateInit2(&def->stream, 15);
} else {
def->flags = FLAG_DEFLATE;
ret = deflateInit2(&def->stream, level, Z_DEFLATED, 15, 8,
Z_DEFAULT_STRATEGY);
}
if (pdbg_likely(ret == Z_OK))
return def;
else {
pmem_free(PMEM_SUBSYS_OTHER, def);
return NULL;
}
}
void pdeflate_destroy(psync_deflate_t *def) {
if (def->flags & FLAG_DEFLATE)
deflateEnd(&def->stream);
else
inflateEnd(&def->stream);
pmem_free(PMEM_SUBSYS_OTHER, def->flushbuff);
pmem_free(PMEM_SUBSYS_OTHER, def);
}
static int psync_deflate_set_out_buff(psync_deflate_t *def) {
uint32_t end;
if (def->bufferendoff - def->bufferstartoff == BUFFER_SIZE)
return -1;
end = def->bufferendoff % BUFFER_SIZE;
def->stream.next_out = def->buffer + end;
if (end < def->bufferstartoff)
def->stream.avail_out = def->bufferstartoff - end;
else
def->stream.avail_out = BUFFER_SIZE - end;
pdbg_assert(def->stream.avail_out != 0);
def->lastout = def->stream.avail_out;
return 0;
}
static int psync_translate_flush(int flush) {
switch (flush) {
case PSYNC_DEFLATE_NOFLUSH:
return Z_NO_FLUSH;
case PSYNC_DEFLATE_FLUSH:
return Z_PARTIAL_FLUSH;
case PSYNC_DEFLATE_FLUSH_END:
return Z_FINISH;
default:
pdbg_logf(D_WARNING, "invalid flush value %d", flush);
return Z_NO_FLUSH;
}
}
static int psync_deflate_call_compressor(psync_deflate_t *def, int flush,
int adjustbe) {
int ret;
pdbg_assert(def->stream.avail_out);
if (def->flags & FLAG_DEFLATE)
ret = deflate(&def->stream, psync_translate_flush(flush));
else
ret = inflate(&def->stream, Z_SYNC_FLUSH);
if (adjustbe)
def->bufferendoff += def->lastout - def->stream.avail_out;
if (def->stream.avail_out)
def->flags &= ~FLAG_MORE_DATA;
else
def->flags |= FLAG_MORE_DATA;
return ret;
}
static int psync_deflate_finish_flush_add_buffer(psync_deflate_t *def,
int flush) {
unsigned char *buff;
uint32_t alloced, used, current;
int ret;
alloced = 4096;
buff = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(unsigned char) * alloced);
current = alloced;
used = 0;
def->flags &= ~FLAG_MORE_DATA;
def->flushbuffoff = 0;
while (1) {
def->stream.next_out = buff + used;
def->stream.avail_out = current;
ret = deflate(&def->stream, psync_translate_flush(flush));
if (ret != Z_OK) {
if (ret != Z_BUF_ERROR)
return ret;
if (used == 0) {
pmem_free(PMEM_SUBSYS_OTHER, buff);
return Z_OK;
}
def->flushbuff = buff;
def->flushbufflen = used;
pdbg_logf(D_NOTICE, "added additional buffer of size %u",
(unsigned)def->flushbufflen);
return Z_OK;
}
if (def->stream.avail_out) {
def->flushbuff = buff;
def->flushbufflen = used + current - def->stream.avail_out;
pdbg_logf(D_NOTICE, "added additional buffer of size %u",
(unsigned)def->flushbufflen);
return Z_OK;
}
used += current;
alloced *= 2;
buff = (unsigned char *)pmem_realloc(PMEM_SUBSYS_OTHER, buff, alloced);
current = alloced - used;
}
}
int pdeflate_write(psync_deflate_t *def, const void *data, int len,
int flush) {
int ret;
if (!len && flush == PSYNC_DEFLATE_NOFLUSH) {
pdbg_logf(D_WARNING, "called with no len and no flush");
return PSYNC_DEFLATE_ERROR;
}
if (def->flushbuff || psync_deflate_set_out_buff(def))
return pdbg_return_const(PSYNC_DEFLATE_FULL);
def->stream.next_in = (unsigned char *)data;
def->stream.avail_in = len;
ret = psync_deflate_call_compressor(def, flush, 1);
if (ret == Z_OK && (def->flags & FLAG_MORE_DATA) &&
!psync_deflate_set_out_buff(def)) {
ret = psync_deflate_call_compressor(def, flush, 1);
if (ret == Z_BUF_ERROR)
ret = Z_OK;
}
if (ret == Z_OK && def->flags & FLAG_DEFLATE &&
flush != PSYNC_DEFLATE_NOFLUSH && (def->flags & FLAG_MORE_DATA))
ret = psync_deflate_finish_flush_add_buffer(def, flush);
if (ret == Z_STREAM_END)
def->flags |= FLAG_STREAM_END;
if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR)
return PSYNC_DEFLATE_ERROR;
else
return len - def->stream.avail_in;
}
int pdeflate_read(psync_deflate_t *def, void *data, int len) {
int ret;
pdbg_assert(def->bufferstartoff <= def->bufferendoff);
if (def->bufferendoff == def->bufferstartoff) {
if (def->flushbuff) {
if (len > def->flushbufflen - def->flushbuffoff)
len = def->flushbufflen - def->flushbuffoff;
memcpy(data, def->flushbuff + def->flushbuffoff, len);
def->flushbuffoff += len;
if (def->flushbuffoff == def->flushbufflen) {
pmem_free(PMEM_SUBSYS_OTHER, def->flushbuff);
def->flushbuff = NULL;
}
return len;
}
if (def->flags & FLAG_MORE_DATA) {
def->stream.next_in = (unsigned char *)"";
def->stream.avail_in = 0;
def->stream.next_out = (unsigned char *)data;
def->stream.avail_out = len;
ret = psync_deflate_call_compressor(def, PSYNC_DEFLATE_NOFLUSH, 0);
switch (ret) {
case Z_BUF_ERROR:
return PSYNC_DEFLATE_NODATA;
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
return pdbg_return_const(PSYNC_DEFLATE_ERROR);
case Z_STREAM_END:
def->flags |= FLAG_STREAM_END;
default:
len -= def->stream.avail_out;
if (len == 0)
return PSYNC_DEFLATE_NODATA;
else
return len;
}
} else
return (def->flags & FLAG_STREAM_END) ? PSYNC_DEFLATE_EOF
: PSYNC_DEFLATE_NODATA;
}
if (len > def->bufferendoff - def->bufferstartoff)
len = def->bufferendoff - def->bufferstartoff;
pdbg_assert(len <= BUFFER_SIZE);
pdbg_assert(def->bufferstartoff <= BUFFER_SIZE);
if (def->bufferstartoff + len <= BUFFER_SIZE)
memcpy(data, def->buffer + def->bufferstartoff, len);
else {
pdbg_assert(len - (BUFFER_SIZE - def->bufferstartoff) ==
def->bufferendoff % BUFFER_SIZE);
memcpy(data, def->buffer + def->bufferstartoff,
BUFFER_SIZE - def->bufferstartoff);
memcpy((char *)data + BUFFER_SIZE - def->bufferstartoff, def->buffer,
len - (BUFFER_SIZE - def->bufferstartoff));
}
def->bufferstartoff += len;
if (def->bufferstartoff == def->bufferendoff)
def->bufferstartoff = def->bufferendoff = 0;
else if (def->bufferstartoff >= BUFFER_SIZE) {
def->bufferstartoff -= BUFFER_SIZE;
pdbg_assert(def->bufferendoff >= BUFFER_SIZE);
def->bufferendoff -= BUFFER_SIZE;
}
return len;
}
int pdeflate_pending(psync_deflate_t *def) {
return def->bufferendoff - def->bufferstartoff +
(def->flushbuff ? def->flushbufflen : 0);
}