673 lines
18 KiB
C
673 lines
18 KiB
C
|
/* compress-io.c
|
||
|
* Copyright 1984-2019 Cisco Systems, Inc.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
/* Dispatch to zlib or LZ4 */
|
||
|
|
||
|
#include "system.h"
|
||
|
#include "zlib.h"
|
||
|
#include "lz4.h"
|
||
|
#include "lz4frame.h"
|
||
|
#include "lz4hc.h"
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#ifdef WIN32
|
||
|
#include <io.h>
|
||
|
# define WIN32_IZE(id) _ ## id
|
||
|
# define GLZ_O_BINARY O_BINARY
|
||
|
#else
|
||
|
# define WIN32_IZE(id) id
|
||
|
# define GLZ_O_BINARY 0
|
||
|
#endif
|
||
|
|
||
|
/* the value of LZ4_OUTPUT_PORT_IN_BUFFER_SIZE was determined
|
||
|
through experimentation on an intel linux server and an intel
|
||
|
osx laptop. smaller sizes result in significantly worse compression
|
||
|
of object files, and larger sizes don't have much beneficial effect.
|
||
|
don't increase the output-port in-buffer size unless you're sure
|
||
|
it reduces object-file size or reduces compression time
|
||
|
significantly. don't decrease it unless you're sure it doesn't
|
||
|
increase object-file size significantly. one buffer of size
|
||
|
LZ4_OUTPUT_PORT_IN_BUFFER_SIZE is allocated per lz4-compressed
|
||
|
output port. another buffer of a closely related size is allocated
|
||
|
per thread. */
|
||
|
#define LZ4_OUTPUT_PORT_IN_BUFFER_SIZE (1 << 18)
|
||
|
|
||
|
/* the values we choose for LZ4_INPUT_PORT_IN_BUFFER_SIZE and
|
||
|
LZ4_INPUT_PORT_OUT_BUFFER_SIZE don't seem to make much difference
|
||
|
in decompression speed, so we keep them fairly small. one buffer
|
||
|
of size LZ4_INPUT_PORT_IN_BUFFER_SIZE and one buffer of size
|
||
|
LZ4_INPUT_PORT_OUT_BUFFER_SIZE are allocated per lz4-compressed
|
||
|
input port. */
|
||
|
#define LZ4_INPUT_PORT_IN_BUFFER_SIZE (1 << 12)
|
||
|
#define LZ4_INPUT_PORT_OUT_BUFFER_SIZE (1 << 14)
|
||
|
|
||
|
typedef struct lz4File_out_r {
|
||
|
LZ4F_preferences_t preferences;
|
||
|
INT fd;
|
||
|
INT out_buffer_size;
|
||
|
INT in_pos;
|
||
|
INT err;
|
||
|
size_t stream_pos;
|
||
|
char in_buffer[LZ4_OUTPUT_PORT_IN_BUFFER_SIZE];
|
||
|
} lz4File_out;
|
||
|
|
||
|
typedef struct lz4File_in_r {
|
||
|
INT fd;
|
||
|
LZ4F_dctx *dctx;
|
||
|
INT in_pos, in_len, out_pos, out_len;
|
||
|
INT frame_ended;
|
||
|
INT err;
|
||
|
size_t stream_pos;
|
||
|
off_t init_pos;
|
||
|
char in_buffer[LZ4_INPUT_PORT_IN_BUFFER_SIZE];
|
||
|
char out_buffer[LZ4_INPUT_PORT_OUT_BUFFER_SIZE];
|
||
|
} lz4File_in;
|
||
|
|
||
|
typedef struct sized_buffer_r {
|
||
|
INT size;
|
||
|
char buffer[0];
|
||
|
} sized_buffer;
|
||
|
|
||
|
/* local functions */
|
||
|
static glzFile glzdopen_output_gz(INT fd, INT compress_level);
|
||
|
static glzFile glzdopen_output_lz4(INT fd, INT compress_level);
|
||
|
static glzFile glzdopen_input_gz(INT fd);
|
||
|
static glzFile glzdopen_input_lz4(INT fd, off_t init_pos);
|
||
|
static INT glzread_lz4(lz4File_in *lz4, void *buffer, UINT count);
|
||
|
static INT glzemit_lz4(lz4File_out *lz4, void *buffer, UINT count);
|
||
|
static INT glzwrite_lz4(lz4File_out *lz4, void *buffer, UINT count);
|
||
|
|
||
|
INT S_zlib_compress_level(INT compress_level) {
|
||
|
switch (compress_level) {
|
||
|
case COMPRESS_MIN:
|
||
|
case COMPRESS_LOW:
|
||
|
return Z_BEST_SPEED;
|
||
|
case COMPRESS_MEDIUM:
|
||
|
return (Z_BEST_SPEED + Z_BEST_COMPRESSION) / 2;
|
||
|
case COMPRESS_HIGH:
|
||
|
return (Z_BEST_SPEED + (3 * Z_BEST_COMPRESSION)) / 4;
|
||
|
case COMPRESS_MAX:
|
||
|
return Z_BEST_COMPRESSION;
|
||
|
default:
|
||
|
S_error1("S_zlib_compress_level", "unexpected compress level ~s", Sinteger(compress_level));
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static glzFile glzdopen_output_gz(INT fd, INT compress_level) {
|
||
|
gzFile gz;
|
||
|
glzFile glz;
|
||
|
INT as_append;
|
||
|
INT level;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
as_append = 0;
|
||
|
#else
|
||
|
as_append = fcntl(fd, F_GETFL) & O_APPEND;
|
||
|
#endif
|
||
|
|
||
|
if ((gz = gzdopen(fd, as_append ? "ab" : "wb")) == Z_NULL) return Z_NULL;
|
||
|
|
||
|
level = S_zlib_compress_level(compress_level);
|
||
|
|
||
|
gzsetparams(gz, level, Z_DEFAULT_STRATEGY);
|
||
|
|
||
|
if ((glz = malloc(sizeof(struct glzFile_r))) == NULL) {
|
||
|
(void)gzclose(gz);
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
glz->fd = fd;
|
||
|
glz->inputp = 0;
|
||
|
glz->format = COMPRESS_GZIP;
|
||
|
glz->gz = gz;
|
||
|
return glz;
|
||
|
}
|
||
|
|
||
|
INT S_lz4_compress_level(INT compress_level) {
|
||
|
switch (compress_level) {
|
||
|
case COMPRESS_MIN:
|
||
|
case COMPRESS_LOW:
|
||
|
return 1;
|
||
|
case COMPRESS_MEDIUM:
|
||
|
return LZ4HC_CLEVEL_MIN;
|
||
|
case COMPRESS_HIGH:
|
||
|
return (LZ4HC_CLEVEL_MIN + LZ4HC_CLEVEL_MAX) / 2;
|
||
|
case COMPRESS_MAX:
|
||
|
return LZ4HC_CLEVEL_MAX;
|
||
|
default:
|
||
|
S_error1("S_lz4_compress_level", "unexpected compress level ~s", Sinteger(compress_level));
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static glzFile glzdopen_output_lz4(INT fd, INT compress_level) {
|
||
|
glzFile glz;
|
||
|
lz4File_out *lz4;
|
||
|
INT level;
|
||
|
|
||
|
level = S_lz4_compress_level(compress_level);
|
||
|
|
||
|
if ((lz4 = malloc(sizeof(lz4File_out))) == NULL) return Z_NULL;
|
||
|
memset(&lz4->preferences, 0, sizeof(LZ4F_preferences_t));
|
||
|
lz4->preferences.compressionLevel = level;
|
||
|
lz4->fd = fd;
|
||
|
lz4->out_buffer_size = (INT)LZ4F_compressFrameBound(LZ4_OUTPUT_PORT_IN_BUFFER_SIZE, &lz4->preferences);
|
||
|
lz4->in_pos = 0;
|
||
|
lz4->err = 0;
|
||
|
lz4->stream_pos = 0;
|
||
|
|
||
|
if ((glz = malloc(sizeof(struct glzFile_r))) == NULL) {
|
||
|
free(lz4);
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
glz->fd = fd;
|
||
|
glz->inputp = 0;
|
||
|
glz->format = COMPRESS_LZ4;
|
||
|
glz->lz4_out = lz4;
|
||
|
return glz;
|
||
|
}
|
||
|
|
||
|
glzFile S_glzdopen_output(INT fd, INT compress_format, INT compress_level) {
|
||
|
switch (compress_format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return glzdopen_output_gz(fd, compress_level);
|
||
|
case COMPRESS_LZ4:
|
||
|
return glzdopen_output_lz4(fd, compress_level);
|
||
|
default:
|
||
|
S_error1("glzdopen_output", "unexpected compress format ~s", Sinteger(compress_format));
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static glzFile glzdopen_input_gz(INT fd) {
|
||
|
gzFile gz;
|
||
|
glzFile glz;
|
||
|
|
||
|
if ((gz = gzdopen(fd, "rb")) == Z_NULL) return Z_NULL;
|
||
|
|
||
|
if ((glz = malloc(sizeof(struct glzFile_r))) == NULL) {
|
||
|
(void)gzclose(gz);
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
glz->fd = fd;
|
||
|
glz->inputp = 1;
|
||
|
glz->format = COMPRESS_GZIP;
|
||
|
glz->gz = gz;
|
||
|
return glz;
|
||
|
}
|
||
|
|
||
|
static glzFile glzdopen_input_lz4(INT fd, off_t init_pos) {
|
||
|
glzFile glz;
|
||
|
LZ4F_dctx *dctx;
|
||
|
LZ4F_errorCode_t r;
|
||
|
lz4File_in *lz4;
|
||
|
|
||
|
r = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION);
|
||
|
if (LZ4F_isError(r))
|
||
|
return Z_NULL;
|
||
|
|
||
|
if ((lz4 = malloc(sizeof(lz4File_in))) == NULL) {
|
||
|
(void)LZ4F_freeDecompressionContext(dctx);
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
lz4->fd = fd;
|
||
|
lz4->dctx = dctx;
|
||
|
lz4->in_pos = 0;
|
||
|
lz4->in_len = 0;
|
||
|
lz4->out_len = 0;
|
||
|
lz4->out_pos = 0;
|
||
|
lz4->frame_ended = 0;
|
||
|
lz4->err = 0;
|
||
|
lz4->stream_pos = 0;
|
||
|
lz4->init_pos = init_pos;
|
||
|
|
||
|
if ((glz = malloc(sizeof(struct glzFile_r))) == NULL) {
|
||
|
(void)LZ4F_freeDecompressionContext(lz4->dctx);
|
||
|
free(lz4);
|
||
|
return Z_NULL;
|
||
|
}
|
||
|
glz->fd = fd;
|
||
|
glz->inputp = 1;
|
||
|
glz->format = COMPRESS_LZ4;
|
||
|
glz->lz4_in = lz4;
|
||
|
return glz;
|
||
|
}
|
||
|
|
||
|
glzFile S_glzdopen_input(INT fd) {
|
||
|
INT r, pos = 0;
|
||
|
unsigned char buffer[4];
|
||
|
off_t init_pos;
|
||
|
|
||
|
/* check for LZ4 magic number, otherwise defer to gzdopen */
|
||
|
|
||
|
if ((init_pos = WIN32_IZE(lseek)(fd, 0, SEEK_CUR)) == -1) return Z_NULL;
|
||
|
|
||
|
while (pos < 4) {
|
||
|
r = WIN32_IZE(read)(fd, (char*)buffer + pos, 4 - pos);
|
||
|
if (r == 0)
|
||
|
break;
|
||
|
else if (r > 0)
|
||
|
pos += r;
|
||
|
#ifdef EINTR
|
||
|
else if (errno == EINTR)
|
||
|
continue;
|
||
|
#endif
|
||
|
else
|
||
|
break; /* error reading */
|
||
|
}
|
||
|
|
||
|
if (pos > 0) {
|
||
|
if (WIN32_IZE(lseek)(fd, init_pos, SEEK_SET) == -1) return Z_NULL;
|
||
|
}
|
||
|
|
||
|
if ((pos == 4)
|
||
|
&& (buffer[0] == 0x04)
|
||
|
&& (buffer[1] == 0x22)
|
||
|
&& (buffer[2] == 0x4d)
|
||
|
&& (buffer[3] == 0x18))
|
||
|
return glzdopen_input_lz4(fd, init_pos);
|
||
|
|
||
|
return glzdopen_input_gz(fd);
|
||
|
}
|
||
|
|
||
|
glzFile S_glzopen_input(const char *path) {
|
||
|
INT fd;
|
||
|
|
||
|
fd = WIN32_IZE(open)(path, O_RDONLY | GLZ_O_BINARY);
|
||
|
|
||
|
if (fd == -1)
|
||
|
return Z_NULL;
|
||
|
else
|
||
|
return S_glzdopen_input(fd);
|
||
|
}
|
||
|
|
||
|
#ifdef WIN32
|
||
|
glzFile S_glzopen_input_w(const wchar_t *path) {
|
||
|
INT fd;
|
||
|
|
||
|
fd = _wopen(path, O_RDONLY | GLZ_O_BINARY);
|
||
|
|
||
|
if (fd == -1)
|
||
|
return Z_NULL;
|
||
|
else
|
||
|
return S_glzdopen_input(fd);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
IBOOL S_glzdirect(glzFile glz) {
|
||
|
if (glz->format == COMPRESS_GZIP)
|
||
|
return gzdirect(glz->gz);
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
INT S_glzclose(glzFile glz) {
|
||
|
INT r = Z_OK, saved_errno = 0;
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
r = gzclose(glz->gz);
|
||
|
break;
|
||
|
case COMPRESS_LZ4:
|
||
|
if (glz->inputp) {
|
||
|
lz4File_in *lz4 = glz->lz4_in;
|
||
|
while (1) {
|
||
|
INT r = WIN32_IZE(close)(lz4->fd);
|
||
|
#ifdef EINTR
|
||
|
if (r < 0 && errno == EINTR) continue;
|
||
|
#endif
|
||
|
if (r == 0) { saved_errno = errno; }
|
||
|
break;
|
||
|
}
|
||
|
(void)LZ4F_freeDecompressionContext(lz4->dctx);
|
||
|
free(lz4);
|
||
|
} else {
|
||
|
lz4File_out *lz4 = glz->lz4_out;
|
||
|
if (lz4->in_pos != 0) {
|
||
|
r = glzemit_lz4(lz4, lz4->in_buffer, lz4->in_pos);
|
||
|
if (r >= 0) r = Z_OK; else { r = Z_ERRNO; saved_errno = errno; }
|
||
|
}
|
||
|
while (1) {
|
||
|
int r1 = WIN32_IZE(close)(lz4->fd);
|
||
|
#ifdef EINTR
|
||
|
if (r1 < 0 && errno == EINTR) continue;
|
||
|
#endif
|
||
|
if (r == Z_OK && r1 < 0) { r = Z_ERRNO; saved_errno = errno; }
|
||
|
break;
|
||
|
}
|
||
|
free(lz4);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
S_error1("S_glzclose", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
}
|
||
|
free(glz);
|
||
|
if (saved_errno) errno = saved_errno;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static INT glzread_lz4(lz4File_in *lz4, void *buffer, UINT count) {
|
||
|
while (lz4->out_pos == lz4->out_len) {
|
||
|
INT in_avail;
|
||
|
|
||
|
in_avail = lz4->in_len - lz4->in_pos;
|
||
|
if (!in_avail) {
|
||
|
while (1) {
|
||
|
in_avail = WIN32_IZE(read)(lz4->fd, lz4->in_buffer, LZ4_INPUT_PORT_IN_BUFFER_SIZE);
|
||
|
if (in_avail >= 0) {
|
||
|
lz4->in_len = in_avail;
|
||
|
lz4->in_pos = 0;
|
||
|
break;
|
||
|
#ifdef EINTR
|
||
|
} else if (errno == EINTR) {
|
||
|
/* try again */
|
||
|
#endif
|
||
|
} else {
|
||
|
lz4->err = Z_ERRNO;
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (in_avail > 0) {
|
||
|
size_t amt, out_len = LZ4_INPUT_PORT_OUT_BUFFER_SIZE, in_len = in_avail;
|
||
|
|
||
|
/* For a large enough result buffer, try to decompress directly
|
||
|
to that buffer: */
|
||
|
if (count >= (out_len >> 1)) {
|
||
|
size_t direct_out_len = count;
|
||
|
|
||
|
if (lz4->frame_ended && lz4->in_buffer[lz4->in_pos] == 0)
|
||
|
return 0; /* count 0 after frame as stream terminator */
|
||
|
|
||
|
amt = LZ4F_decompress(lz4->dctx,
|
||
|
buffer, &direct_out_len,
|
||
|
lz4->in_buffer + lz4->in_pos, &in_len,
|
||
|
NULL);
|
||
|
lz4->frame_ended = (amt == 0);
|
||
|
|
||
|
if (LZ4F_isError(amt)) {
|
||
|
lz4->err = Z_STREAM_ERROR;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
lz4->in_pos += (INT)in_len;
|
||
|
|
||
|
if (direct_out_len) {
|
||
|
lz4->stream_pos += direct_out_len;
|
||
|
return (INT)direct_out_len;
|
||
|
}
|
||
|
|
||
|
in_len = in_avail - in_len;
|
||
|
}
|
||
|
|
||
|
if (in_len > 0) {
|
||
|
if (lz4->frame_ended && lz4->in_buffer[lz4->in_pos] == 0)
|
||
|
return 0; /* count 0 after frame as stream terminator */
|
||
|
|
||
|
amt = LZ4F_decompress(lz4->dctx,
|
||
|
lz4->out_buffer, &out_len,
|
||
|
lz4->in_buffer + lz4->in_pos, &in_len,
|
||
|
NULL);
|
||
|
lz4->frame_ended = (amt == 0);
|
||
|
|
||
|
if (LZ4F_isError(amt)) {
|
||
|
lz4->err = Z_STREAM_ERROR;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
lz4->in_pos += (INT)in_len;
|
||
|
lz4->out_len = (INT)out_len;
|
||
|
lz4->out_pos = 0;
|
||
|
}
|
||
|
} else {
|
||
|
/* EOF on read */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lz4->out_pos < lz4->out_len) {
|
||
|
UINT amt = lz4->out_len - lz4->out_pos;
|
||
|
if (amt > count) amt = count;
|
||
|
memcpy(buffer, lz4->out_buffer + lz4->out_pos, amt);
|
||
|
lz4->out_pos += amt;
|
||
|
lz4->stream_pos += amt;
|
||
|
return amt;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
INT S_glzread(glzFile glz, void *buffer, UINT count) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return gzread(glz->gz, buffer, count);
|
||
|
case COMPRESS_LZ4:
|
||
|
return glzread_lz4(glz->lz4_in, buffer, count);
|
||
|
default:
|
||
|
S_error1("S_glzread", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static INT glzemit_lz4(lz4File_out *lz4, void *buffer, UINT count) {
|
||
|
ptr tc = get_thread_context();
|
||
|
sized_buffer *cached_out_buffer;
|
||
|
char *out_buffer;
|
||
|
INT out_len, out_pos;
|
||
|
INT r = 0;
|
||
|
|
||
|
/* allocate one out_buffer (per thread) since we don't need one for each file.
|
||
|
the buffer is freed by destroy_thread. */
|
||
|
if ((cached_out_buffer = LZ4OUTBUFFER(tc)) == NULL || cached_out_buffer->size < lz4->out_buffer_size) {
|
||
|
if (cached_out_buffer != NULL) free(cached_out_buffer);
|
||
|
if ((LZ4OUTBUFFER(tc) = cached_out_buffer = malloc(sizeof(sized_buffer) + lz4->out_buffer_size)) == NULL) return -1;
|
||
|
cached_out_buffer->size = lz4->out_buffer_size;
|
||
|
}
|
||
|
out_buffer = cached_out_buffer->buffer;
|
||
|
|
||
|
out_len = (INT)LZ4F_compressFrame(out_buffer, lz4->out_buffer_size,
|
||
|
buffer, count,
|
||
|
&lz4->preferences);
|
||
|
if (LZ4F_isError(out_len)) {
|
||
|
lz4->err = Z_STREAM_ERROR;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
out_pos = 0;
|
||
|
while (out_pos < out_len) {
|
||
|
r = WIN32_IZE(write)(lz4->fd, out_buffer + out_pos, out_len - out_pos);
|
||
|
if (r >= 0)
|
||
|
out_pos += r;
|
||
|
#ifdef EINTR
|
||
|
else if (errno == EINTR)
|
||
|
continue;
|
||
|
#endif
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static INT glzwrite_lz4(lz4File_out *lz4, void *buffer, UINT count) {
|
||
|
UINT amt; INT r;
|
||
|
|
||
|
if ((amt = LZ4_OUTPUT_PORT_IN_BUFFER_SIZE - lz4->in_pos) > count) amt = count;
|
||
|
|
||
|
if (amt == LZ4_OUTPUT_PORT_IN_BUFFER_SIZE) {
|
||
|
/* full buffer coming from input...skip the memcpy */
|
||
|
if ((r = glzemit_lz4(lz4, buffer, LZ4_OUTPUT_PORT_IN_BUFFER_SIZE)) < 0) return 0;
|
||
|
} else {
|
||
|
memcpy(lz4->in_buffer + lz4->in_pos, buffer, amt);
|
||
|
if ((lz4->in_pos += amt) == LZ4_OUTPUT_PORT_IN_BUFFER_SIZE) {
|
||
|
lz4->in_pos = 0;
|
||
|
if ((r = glzemit_lz4(lz4, lz4->in_buffer, LZ4_OUTPUT_PORT_IN_BUFFER_SIZE)) < 0) return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lz4->stream_pos += amt;
|
||
|
return amt;
|
||
|
}
|
||
|
|
||
|
INT S_glzwrite(glzFile glz, void *buffer, UINT count) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return gzwrite(glz->gz, buffer, count);
|
||
|
case COMPRESS_LZ4:
|
||
|
return glzwrite_lz4(glz->lz4_out, buffer, count);
|
||
|
default:
|
||
|
S_error1("S_glzwrite", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
long S_glzseek(glzFile glz, long offset, INT whence) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return gzseek(glz->gz, offset, whence);
|
||
|
case COMPRESS_LZ4:
|
||
|
if (glz->inputp) {
|
||
|
lz4File_in *lz4 = glz->lz4_in;
|
||
|
if (whence == SEEK_CUR)
|
||
|
offset += (long)lz4->stream_pos;
|
||
|
if (offset < 0)
|
||
|
offset = 0;
|
||
|
if ((size_t)offset < lz4->stream_pos) {
|
||
|
/* rewind and read from start */
|
||
|
if (WIN32_IZE(lseek)(lz4->fd, lz4->init_pos, SEEK_SET) < 0) {
|
||
|
lz4->err = Z_ERRNO;
|
||
|
return -1;
|
||
|
}
|
||
|
LZ4F_resetDecompressionContext(lz4->dctx);
|
||
|
lz4->in_pos = 0;
|
||
|
lz4->in_len = 0;
|
||
|
lz4->out_len = 0;
|
||
|
lz4->out_pos = 0;
|
||
|
lz4->err = 0;
|
||
|
lz4->stream_pos = 0;
|
||
|
}
|
||
|
while ((size_t)offset > lz4->stream_pos) {
|
||
|
static char buffer[1024];
|
||
|
size_t amt = (size_t)offset - lz4->stream_pos;
|
||
|
if (amt > sizeof(buffer)) amt = sizeof(buffer);
|
||
|
if (glzread_lz4(lz4, buffer, (UINT)amt) < 0)
|
||
|
return -1;
|
||
|
}
|
||
|
return (long)lz4->stream_pos;
|
||
|
} else {
|
||
|
lz4File_out *lz4 = glz->lz4_out;
|
||
|
if (whence == SEEK_CUR)
|
||
|
offset += (long)lz4->stream_pos;
|
||
|
if (offset >= 0) {
|
||
|
while ((size_t)offset > lz4->stream_pos) {
|
||
|
size_t amt = (size_t)offset - lz4->stream_pos;
|
||
|
if (amt > 8) amt = 8;
|
||
|
if (glzwrite_lz4(lz4, "\0\0\0\0\0\0\0\0", (UINT)amt) < 0)
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return (long)lz4->stream_pos;
|
||
|
}
|
||
|
default:
|
||
|
S_error1("S_glzseek", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INT S_glzgetc(glzFile glz) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return gzgetc(glz->gz);
|
||
|
case COMPRESS_LZ4:
|
||
|
{
|
||
|
unsigned char buffer[1];
|
||
|
INT r;
|
||
|
r = S_glzread(glz, buffer, 1);
|
||
|
if (r == 1)
|
||
|
return buffer[0];
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
default:
|
||
|
S_error1("S_glzgetc", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INT S_glzungetc(INT c, glzFile glz) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
return gzungetc(c, glz->gz);
|
||
|
case COMPRESS_LZ4:
|
||
|
{
|
||
|
lz4File_in *lz4 = glz->lz4_in;
|
||
|
if (lz4->out_len == 0)
|
||
|
lz4->out_len = lz4->out_pos = 1;
|
||
|
if (lz4->out_pos) {
|
||
|
lz4->out_pos--;
|
||
|
lz4->out_buffer[lz4->out_pos] = c;
|
||
|
lz4->stream_pos--;
|
||
|
return c;
|
||
|
} else {
|
||
|
/* support ungetc only just after a getc, in which case there
|
||
|
should have been room */
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
default:
|
||
|
S_error1("S_glzungetc", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INT S_glzrewind(glzFile glz) {
|
||
|
return S_glzseek(glz, 0, SEEK_SET);
|
||
|
}
|
||
|
|
||
|
void S_glzerror(glzFile glz, INT *errnum) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
(void)gzerror(glz->gz, errnum);
|
||
|
break;
|
||
|
case COMPRESS_LZ4:
|
||
|
if (glz->inputp)
|
||
|
*errnum = glz->lz4_in->err;
|
||
|
else
|
||
|
*errnum = glz->lz4_out->err;
|
||
|
break;
|
||
|
default:
|
||
|
S_error1("S_glzerror", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
*errnum = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void S_glzclearerr(glzFile glz) {
|
||
|
switch (glz->format) {
|
||
|
case COMPRESS_GZIP:
|
||
|
gzclearerr(glz->gz);
|
||
|
break;
|
||
|
case COMPRESS_LZ4:
|
||
|
if (glz->inputp)
|
||
|
glz->lz4_in->err = 0;
|
||
|
else
|
||
|
glz->lz4_out->err = 0;
|
||
|
break;
|
||
|
default:
|
||
|
S_error1("S_glzerror", "unexpected compress format ~s", Sinteger(glz->format));
|
||
|
}
|
||
|
}
|