summaryrefslogtreecommitdiffstats
path: root/filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'filter.c')
-rw-r--r--filter.c265
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
+};