diff options
Diffstat (limited to 'filter.c')
-rw-r--r-- | filter.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/filter.c b/filter.c new file mode 100644 index 0000000..5b6c158 --- /dev/null +++ b/filter.c @@ -0,0 +1,265 @@ +#include "php.h" +#include "lzf.h" + +#define LZF_BLOCKSIZE (1024 * 64 - 1) + +typedef struct { + char signature[3]; + char usize[2]; +} lzf_header_uncompressed; + +typedef struct { + char signature[3]; + char csize[2]; + char usize[2]; +} lzf_header_compressed; + +typedef struct _php_lzf_compress_filter { + int persistent; + char *buffer; + size_t buffer_pos; +} php_lzf_filter_state; + +static void fill_header_compressed(lzf_header_compressed *header, size_t compressed, size_t uncompressed) +{ + /* Copied from liblzf/lzf.c */ + + header->signature[0] = 'Z'; + header->signature[1] = 'V'; + header->signature[2] = 1; + header->csize[0] = compressed >> 8; + header->csize[1] = compressed & 0xff; + header->usize[0] = uncompressed >> 8; + header->usize[1] = uncompressed & 0xff; +} + +static void fill_header_uncompressed(lzf_header_uncompressed *header, size_t uncompressed) +{ + /* Copied from liblzf/lzf.c */ + + header->signature[0] = 'Z'; + header->signature[1] = 'V'; + header->signature[2] = 0; + header->usize[0] = uncompressed >> 8; + header->usize[1] = uncompressed & 0xff; +} + +static int php_lzf_filter_state_ctor(php_lzf_filter_state *inst, int persistent) +{ + inst->persistent = persistent; + inst->buffer = pemalloc(LZF_BLOCKSIZE, persistent); + inst->buffer_pos = 0; + + return SUCCESS; +} + +static void php_lzf_filter_state_dtor(php_lzf_filter_state *inst TSRMLS_DC) +{ + pefree(inst->buffer, inst->persistent); +} + +static int lzf_compress_filter_append_bucket( + php_stream *stream, + php_stream_filter_status_t *exit_status, + php_lzf_filter_state *inst, + php_stream_bucket_brigade *buckets_out, + int persistent TSRMLS_DC) +{ + int status; + size_t buffer_size; + php_stream_bucket *new_bucket; + char *output_buffer; + + /* Allocate buffer with a size of data and (larger) header */ + output_buffer = pemalloc(inst->buffer_pos + sizeof(lzf_header_compressed), persistent); + + if (!output_buffer) + goto fail; + + /* Try to compress data. */ + status = lzf_compress(inst->buffer, inst->buffer_pos, output_buffer + sizeof(lzf_header_compressed), inst->buffer_pos); + + /* + * If we were able to compress data, write compressed block. Otherwise + * use uncompressed block. + */ + if (status > 0) { + output_buffer = perealloc(output_buffer, status + sizeof(lzf_header_compressed), persistent); + fill_header_compressed((lzf_header_compressed *) output_buffer, status, inst->buffer_pos); + buffer_size = status + sizeof(lzf_header_compressed); + } else { + /* Pessimistic case - we still need to memcpy() data */ + output_buffer = perealloc(output_buffer, inst->buffer_pos + sizeof(lzf_header_uncompressed), persistent); + fill_header_uncompressed((lzf_header_uncompressed *) output_buffer, inst->buffer_pos); + memcpy(output_buffer + sizeof(lzf_header_uncompressed), inst->buffer, inst->buffer_pos); + buffer_size = inst->buffer_pos + sizeof(lzf_header_uncompressed); + } + + /* Create new bucket and append it */ + new_bucket = php_stream_bucket_new(stream, output_buffer, buffer_size, 1, 0 TSRMLS_CC); + if (!new_bucket) + goto fail_free_buffer; + + php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); + + /* Clear our buffer */ + inst->buffer_pos = 0; + + /* Change exit status */ + *exit_status = PSFS_PASS_ON; + + return SUCCESS; + +fail_free_buffer: + pefree(output_buffer, persistent); +fail: + return FAILURE; +} + +static int lzf_compress_append_data( + php_stream *stream, + php_stream_filter_status_t *exit_status, + php_stream_bucket_brigade *buckets_out, + php_lzf_filter_state *inst, + const char *input_buffer, + size_t input_buffer_len, + size_t *consumed, + int persistent TSRMLS_DC) +{ + size_t free_buffer; + size_t bytes_to_copy; + + /* As long as there are data in the input buffer... */ + while (input_buffer_len) { + free_buffer = LZF_BLOCKSIZE - inst->buffer_pos; /* Free space in buffer */ + bytes_to_copy = MIN(free_buffer, input_buffer_len); /* Bytes to copy into buffer */ + + /* ... copy as many bytes into buffer as possible */ + memcpy(inst->buffer + inst->buffer_pos, input_buffer, bytes_to_copy); + inst->buffer_pos += bytes_to_copy; + input_buffer += bytes_to_copy; + input_buffer_len -= bytes_to_copy; + (*consumed) += bytes_to_copy; + + /* If the buffer is full, we need to flush it */ + if (inst->buffer_pos == LZF_BLOCKSIZE) { + if (lzf_compress_filter_append_bucket(stream, exit_status, inst, buckets_out, persistent TSRMLS_CC) != SUCCESS) + return FAILURE; + } + } + + return SUCCESS; +} + +static php_stream_filter_status_t lzf_compress_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags TSRMLS_DC) +{ + size_t consumed = 0; + php_lzf_filter_state *inst = (php_lzf_filter_state *) thisfilter->abstract; + php_stream_filter_status_t exit_status = PSFS_FEED_ME; + php_stream_bucket *bucket = NULL; + + while (buckets_in->head) { + bucket = buckets_in->head; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + + if (lzf_compress_append_data(stream, &exit_status, buckets_out, inst, bucket->buf, bucket->buflen, &consumed, + php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) + goto fail_free_bucket; + + php_stream_bucket_delref(bucket TSRMLS_CC); + } + + if (bytes_consumed) + *bytes_consumed = consumed; + + if (flags & PSFS_FLAG_FLUSH_CLOSE) { + if (lzf_compress_filter_append_bucket(stream, &exit_status, inst, buckets_out, php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) + goto fail; + } + + return exit_status; + +fail_free_bucket: + if (bucket != NULL) + php_stream_bucket_delref(bucket TSRMLS_CC); +fail: + return PSFS_ERR_FATAL; +} + +static php_stream_filter_status_t lzf_decompress_filter( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags TSRMLS_DC) +{ + return PSFS_PASS_ON; +} + +static void lzf_filter_state_dtor(php_stream_filter *thisfilter TSRMLS_DC) +{ + assert(thisfilter->abstract != NULL); + + php_lzf_filter_state_dtor((php_lzf_filter_state *) thisfilter->abstract TSRMLS_CC); + pefree(thisfilter->abstract, ((php_lzf_filter_state *) thisfilter->abstract)->persistent); +} + +static php_stream_filter_ops lzf_compress_ops = { + lzf_compress_filter, + lzf_filter_state_dtor, + "lzf.compress" +}; + +static php_stream_filter_ops lzf_decompress_ops = { + lzf_decompress_filter, + lzf_filter_state_dtor, + "lzf.decompress" +}; + +static php_stream_filter *lzf_compress_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) +{ + php_lzf_filter_state *inst; + + inst = pemalloc(sizeof(php_lzf_filter_state), persistent); + if (inst == NULL) + return NULL; + + if (php_lzf_filter_state_ctor(inst, persistent) != SUCCESS) { + pefree(inst, persistent); + return NULL; + } + + return php_stream_filter_alloc(&lzf_compress_ops, inst, persistent); +} + +static php_stream_filter *lzf_decompress_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) +{ + php_lzf_filter_state *inst; + + inst = pemalloc(sizeof(php_lzf_filter_state), persistent); + if (inst == NULL) + return NULL; + + if (php_lzf_filter_state_ctor(inst, persistent) != SUCCESS) { + pefree(inst, persistent); + return NULL; + } + + return php_stream_filter_alloc(&lzf_decompress_ops, inst, persistent); +} + +php_stream_filter_factory php_lzf_compress_filter_factory = { + lzf_compress_filter_create +}; + +php_stream_filter_factory php_lzf_decompress_filter_factory = { + lzf_decompress_filter_create +}; |