Permalink
Browse files

The only place blocking is really needed is just before calling

the client write functions.  So I've moved all of the blocking
code (that used to be duplicated in every compression filter)
into archive_write.c in the code that wraps the client callbacks.
As a result, add_filter_none is a true no-op.

SVN-Revision: 1936
  • Loading branch information...
1 parent 0eb2fe9 commit 992610f1dd74565cda8df166f70f0d8abf4abda7 @kientzle kientzle committed Feb 21, 2010
View
@@ -37,6 +37,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
@@ -68,6 +71,13 @@ static int _archive_write_header(struct archive *, struct archive_entry *);
static int _archive_write_finish_entry(struct archive *);
static ssize_t _archive_write_data(struct archive *, const void *, size_t);
+struct archive_none {
+ size_t buffer_size;
+ size_t avail;
+ char *buffer;
+ char *next;
+};
+
static struct archive_vtable *
archive_write_vtable(void)
{
@@ -366,38 +376,146 @@ static int
archive_write_client_open(struct archive_write_filter *f)
{
struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state;
+ void *buffer;
+ size_t buffer_size;
+
+ f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);
+ f->bytes_in_last_block =
+ archive_write_get_bytes_in_last_block(f->archive);
+ buffer_size = f->bytes_per_block;
+
+ state = (struct archive_none *)calloc(1, sizeof(*state));
+ buffer = (char *)malloc(buffer_size);
+ if (state == NULL || buffer == NULL) {
+ free(state);
+ free(buffer);
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for output buffering");
+ return (ARCHIVE_FATAL);
+ }
+
+ state->buffer_size = buffer_size;
+ state->buffer = buffer;
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ f->data = state;
+
if (a->client_opener == NULL)
return (ARCHIVE_OK);
- return (a->client_opener(f->archive, f->data));
+ return (a->client_opener(f->archive, a->client_data));
}
static int
archive_write_client_write(struct archive_write_filter *f,
const void *_buff, size_t length)
{
struct archive_write *a = (struct archive_write *)f->archive;
- const char *buff = _buff;
-
- while (length > 0) {
- ssize_t written
- = (a->client_writer(f->archive, f->data, buff, length));
- if (written < 0)
- return ((int)written);
- if (written == 0)
- return (ARCHIVE_FATAL);
- buff += written;
- length -= written;
- }
- return (ARCHIVE_OK);
+ struct archive_none *state = (struct archive_none *)f->data;
+ const char *buff = (const char *)_buff;
+ ssize_t remaining, to_copy;
+ ssize_t bytes_written;
+
+ remaining = length;
+
+ /*
+ * If there is no buffer for blocking, just pass the data
+ * straight through to the client write callback. In
+ * particular, this supports "no write delay" operation for
+ * special applications. Just set the block size to zero.
+ */
+ if (state->buffer_size == 0) {
+ while (remaining > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, remaining);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ remaining -= bytes_written;
+ buff += bytes_written;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* If the copy buffer isn't empty, try to fill it. */
+ if (state->avail < state->buffer_size) {
+ /* If buffer is not empty... */
+ /* ... copy data into buffer ... */
+ to_copy = (remaining > state->avail) ?
+ state->avail : remaining;
+ memcpy(state->next, buff, to_copy);
+ state->next += to_copy;
+ state->avail -= to_copy;
+ buff += to_copy;
+ remaining -= to_copy;
+ /* ... if it's full, write it out. */
+ if (state->avail == 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, state->buffer, state->buffer_size);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ /* XXX TODO: if bytes_written < state->buffer_size */
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ }
+ }
+
+ while (remaining > state->buffer_size) {
+ /* Write out full blocks directly to client. */
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, state->buffer_size);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ buff += bytes_written;
+ remaining -= bytes_written;
+ }
+
+ if (remaining > 0) {
+ /* Copy last bit into copy buffer. */
+ memcpy(state->next, buff, remaining);
+ state->next += remaining;
+ state->avail -= remaining;
+ }
+ return (ARCHIVE_OK);
}
static int
archive_write_client_close(struct archive_write_filter *f)
{
struct archive_write *a = (struct archive_write *)f->archive;
- if (a->client_closer == NULL)
- return (ARCHIVE_OK);
- return (a->client_closer(f->archive, f->data));
+ struct archive_none *state = (struct archive_none *)f->data;
+ ssize_t block_length;
+ ssize_t target_block_length;
+ ssize_t bytes_written;
+ int ret = ARCHIVE_OK;
+
+ /* If there's pending data, pad and write the last block */
+ if (state->next != state->buffer) {
+ block_length = state->buffer_size - state->avail;
+
+ /* Tricky calculation to determine size of last block */
+ if (a->bytes_in_last_block <= 0)
+ /* Default or Zero: pad to full block */
+ target_block_length = a->bytes_per_block;
+ else
+ /* Round to next multiple of bytes_in_last_block. */
+ target_block_length = a->bytes_in_last_block *
+ ( (block_length + a->bytes_in_last_block - 1) /
+ a->bytes_in_last_block);
+ if (target_block_length > a->bytes_per_block)
+ target_block_length = a->bytes_per_block;
+ if (block_length < target_block_length) {
+ memset(state->next, 0,
+ target_block_length - block_length);
+ block_length = target_block_length;
+ }
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, state->buffer, block_length);
+ ret = bytes_written <= 0 ? ARCHIVE_FATAL : ARCHIVE_OK;
+ }
+ free(state->buffer);
+ free(state);
+ a->client_data = NULL;
+ return (ret);
}
/*
@@ -422,9 +540,9 @@ archive_write_open(struct archive *_a, void *client_data,
a->client_writer = writer;
a->client_opener = opener;
a->client_closer = closer;
+ a->client_data = client_data;
client_filter = __archive_write_allocate_filter(_a);
- client_filter->data = client_data;
client_filter->open = archive_write_client_open;
client_filter->write = archive_write_client_write;
client_filter->close = archive_write_client_close;
@@ -241,21 +241,15 @@ static int
archive_compressor_bzip2_close(struct archive_write_filter *f)
{
struct private_data *data = (struct private_data *)f->data;
- ssize_t bytes_written;
- int ret;
-
- if (data == NULL)
- return (ARCHIVE_OK);
+ int ret, r1;
/* Finish compression cycle. */
ret = drive_compressor(f, data, 1);
if (ret == ARCHIVE_OK) {
/* Write the last block */
- bytes_written = __archive_write_filter(f->next_filter,
+ ret = __archive_write_filter(f->next_filter,
data->compressed,
data->compressed_buffer_size - data->stream.avail_out);
- if (bytes_written <= 0)
- ret = ARCHIVE_FATAL;
}
switch (BZ2_bzCompressEnd(&(data->stream))) {
@@ -266,7 +260,9 @@ archive_compressor_bzip2_close(struct archive_write_filter *f)
"Failed to clean up compressor");
ret = ARCHIVE_FATAL;
}
- return (ARCHIVE_OK);
+
+ r1 = __archive_write_close_filter(f->next_filter);
+ return (r1 < ret ? r1 : ret);
}
static int
@@ -430,6 +430,7 @@ archive_compressor_compress_close(struct archive_write_filter *f)
ret = __archive_write_filter(f->next_filter,
state->compressed, state->compressed_offset);
cleanup:
+ ret = __archive_write_close_filter(f->next_filter);
free(state->compressed);
free(state);
return (ret);
@@ -254,24 +254,17 @@ archive_compressor_gzip_close(struct archive_write_filter *f)
{
unsigned char trailer[8];
struct private_data *data = (struct private_data *)f->data;
- int ret;
-
- ret = 0;
- if (data != NULL) {
- /* Finish compression cycle */
- ret = drive_compressor(f, data, 1);
- if (ret != ARCHIVE_OK)
- goto cleanup;
+ int ret, r1;
+ /* Finish compression cycle */
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
/* Write the last compressed data. */
ret = __archive_write_filter(f->next_filter,
data->compressed,
data->compressed_buffer_size - data->stream.avail_out);
- if (ret != ARCHIVE_OK) {
- ret = ARCHIVE_FATAL;
- goto cleanup;
- }
-
+ }
+ if (ret == ARCHIVE_OK) {
/* Build and write out 8-byte trailer. */
trailer[0] = (data->crc)&0xff;
trailer[1] = (data->crc >> 8)&0xff;
@@ -282,20 +275,18 @@ archive_compressor_gzip_close(struct archive_write_filter *f)
trailer[6] = (data->total_in >> 16)&0xff;
trailer[7] = (data->total_in >> 24)&0xff;
ret = __archive_write_filter(f->next_filter, trailer, 8);
- if (ret != ARCHIVE_OK)
- ret = ARCHIVE_FATAL;
+ }
- cleanup:
- switch (deflateEnd(&(data->stream))) {
- case Z_OK:
- break;
- default:
- archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
- "Failed to clean up compressor");
- ret = ARCHIVE_FATAL;
- }
+ switch (deflateEnd(&(data->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
}
- return (ret);
+ r1 = __archive_write_close_filter(f->next_filter);
+ return (r1 < ret ? r1 : ret);
}
static int
Oops, something went wrong.

0 comments on commit 992610f

Please sign in to comment.