Revised the Delta filter implementation. The initialization
authorLasse Collin <lasse.collin@tukaani.org>
Sat, 19 Jan 2008 13:19:21 +0000 (15:19 +0200)
committerLasse Collin <lasse.collin@tukaani.org>
Sat, 19 Jan 2008 13:19:21 +0000 (15:19 +0200)
function is still shared between encoder and decoder, but the
actual coding is in separate files for encoder and decoder.

There are now separate functions for the actual delta
calculation depending on if Delta is the last filter in the
chain or not. If it is the last, the new code copies the
data from input to output buffer and does the delta
calculation at the same time. The old code first copied the
data, then did the delta in the target buffer, which required
reading through the data twice.

Support for LZMA_SYNC_FLUSH was added to the Delta encoder.
This doesn't change anything in the file format.

src/liblzma/common/Makefile.am
src/liblzma/common/delta_coder.c [deleted file]
src/liblzma/common/delta_common.c [new file with mode: 0644]
src/liblzma/common/delta_common.h [new file with mode: 0644]
src/liblzma/common/delta_decoder.c [new file with mode: 0644]
src/liblzma/common/delta_decoder.h [moved from src/liblzma/common/delta_coder.h with 77% similarity]
src/liblzma/common/delta_encoder.c [new file with mode: 0644]
src/liblzma/common/delta_encoder.h [new file with mode: 0644]
src/liblzma/common/raw_decoder.c
src/liblzma/common/raw_encoder.c

index a2eacfd..048f4c7 100644 (file)
@@ -48,8 +48,18 @@ endif
 
 if COND_FILTER_DELTA
 libcommon_la_SOURCES += \
-       delta_coder.c \
-       delta_coder.h
+       delta_common.c \
+       delta_common.h
+if COND_MAIN_ENCODER
+libcommon_la_SOURCES += \
+       delta_encoder.c \
+       delta_encoder.h
+endif
+if COND_MAIN_DECODER
+libcommon_la_SOURCES += \
+       delta_decoder.c \
+       delta_decoder.h
+endif
 endif
 
 if COND_MAIN_ENCODER
diff --git a/src/liblzma/common/delta_coder.c b/src/liblzma/common/delta_coder.c
deleted file mode 100644 (file)
index 9b260a0..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-//
-/// \file       delta_coder.c
-/// \brief      Encoder and decoder for the Delta filter
-//
-//  Copyright (C) 2007 Lasse Collin
-//
-//  This library is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU Lesser General Public
-//  License as published by the Free Software Foundation; either
-//  version 2.1 of the License, or (at your option) any later version.
-//
-//  This library is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  Lesser General Public License for more details.
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#include "delta_coder.h"
-
-
-struct lzma_coder_s {
-       /// Next coder in the chain
-       lzma_next_coder next;
-
-       /// Uncompressed size - This is needed when we are the last
-       /// filter in the chain.
-       lzma_vli uncompressed_size;
-
-       /// Delta distance
-       size_t distance;
-
-       /// True if we are encoding; false if decoding
-       bool is_encoder;
-
-       /// Position in history[]
-       uint8_t pos;
-
-       /// Buffer to hold history of the original data
-       uint8_t history[LZMA_DELTA_DISTANCE_MAX];
-};
-
-
-static void
-encode_buffer(lzma_coder *coder, uint8_t *buffer, size_t size)
-{
-       const size_t distance = coder->distance;
-
-       for (size_t i = 0; i < size; ++i) {
-               const uint8_t tmp = coder->history[
-                               (distance + coder->pos) & 0xFF];
-               coder->history[coder->pos--] = buffer[i];
-               buffer[i] -= tmp;
-       }
-
-       return;
-}
-
-
-static void
-decode_buffer(lzma_coder *coder, uint8_t *buffer, size_t size)
-{
-       const size_t distance = coder->distance;
-
-       for (size_t i = 0; i < size; ++i) {
-               buffer[i] += coder->history[(distance + coder->pos) & 0xFF];
-               coder->history[coder->pos--] = buffer[i];
-       }
-
-       return;
-}
-
-
-static lzma_ret
-delta_code(lzma_coder *coder, lzma_allocator *allocator,
-               const uint8_t *restrict in, size_t *restrict in_pos,
-               size_t in_size, uint8_t *restrict out,
-               size_t *restrict out_pos, size_t out_size, lzma_action action)
-{
-       const size_t out_start = *out_pos;
-       size_t size;
-       lzma_ret ret;
-
-       if (coder->next.code == NULL) {
-               if (!coder->is_encoder) {
-                       // Limit in_size so that we don't copy too much.
-                       if ((lzma_vli)(in_size - *in_pos)
-                                       > coder->uncompressed_size)
-                               in_size = *in_pos + (size_t)(
-                                               coder->uncompressed_size);
-               }
-
-               size = bufcpy(in, in_pos, in_size, out, out_pos, out_size);
-
-               if (coder->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
-                       coder->uncompressed_size -= size;
-
-               // action can be LZMA_FINISH only in the encoder.
-               ret = (action == LZMA_FINISH && *in_pos == in_size)
-                                       || coder->uncompressed_size == 0
-                               ? LZMA_STREAM_END : LZMA_OK;
-
-       } else {
-               ret = coder->next.code(coder->next.coder, allocator,
-                               in, in_pos, in_size, out, out_pos, out_size,
-                               action);
-               if (ret != LZMA_OK && ret != LZMA_STREAM_END)
-                       return ret;
-
-               size = *out_pos - out_start;
-       }
-
-       if (coder->is_encoder)
-               encode_buffer(coder, out + out_start, size);
-       else
-               decode_buffer(coder, out + out_start, size);
-
-       return ret;
-}
-
-
-static void
-delta_coder_end(lzma_coder *coder, lzma_allocator *allocator)
-{
-       lzma_next_coder_end(&coder->next, allocator);
-       lzma_free(coder, allocator);
-       return;
-}
-
-
-static lzma_ret
-delta_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
-               const lzma_filter_info *filters, bool is_encoder)
-{
-       // Allocate memory for the decoder if needed.
-       if (next->coder == NULL) {
-               next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
-               if (next->coder == NULL)
-                       return LZMA_MEM_ERROR;
-
-               next->code = &delta_code;
-               next->end = &delta_coder_end;
-               next->coder->next = LZMA_NEXT_CODER_INIT;
-       }
-
-       // Copy Uncompressed Size which is used to limit the output size.
-       next->coder->uncompressed_size = filters[0].uncompressed_size;
-
-       // The coder acts slightly differently as encoder and decoder.
-       next->coder->is_encoder = is_encoder;
-
-       // Set the delta distance.
-       if (filters[0].options == NULL)
-               return LZMA_PROG_ERROR;
-       next->coder->distance = ((lzma_options_delta *)(filters[0].options))
-                       ->distance;
-       if (next->coder->distance < LZMA_DELTA_DISTANCE_MIN
-                       || next->coder->distance > LZMA_DELTA_DISTANCE_MAX)
-               return LZMA_HEADER_ERROR;
-
-       // Initialize the rest of the variables.
-       next->coder->pos = 0;
-       memzero(next->coder->history, LZMA_DELTA_DISTANCE_MAX);
-
-       // Initialize the next decoder in the chain, if any.
-       return lzma_next_filter_init(&next->coder->next,
-                               allocator, filters + 1);
-}
-
-
-#ifdef HAVE_ENCODER
-extern lzma_ret
-lzma_delta_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
-               const lzma_filter_info *filters)
-{
-       return delta_coder_init(next, allocator, filters, true);
-}
-#endif
-
-
-#ifdef HAVE_DECODER
-extern lzma_ret
-lzma_delta_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
-               const lzma_filter_info *filters)
-{
-       return delta_coder_init(next, allocator, filters, false);
-}
-#endif
diff --git a/src/liblzma/common/delta_common.c b/src/liblzma/common/delta_common.c
new file mode 100644 (file)
index 0000000..de27b5a
--- /dev/null
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       delta_common.c
+/// \brief      Common stuff for Delta encoder and decoder
+//
+//  Copyright (C) 2007 Lasse Collin
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "delta_common.h"
+
+
+static void
+delta_coder_end(lzma_coder *coder, lzma_allocator *allocator)
+{
+       lzma_next_coder_end(&coder->next, allocator);
+       lzma_free(coder, allocator);
+       return;
+}
+
+
+extern lzma_ret
+lzma_delta_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
+               const lzma_filter_info *filters, lzma_code_function code)
+{
+       // Allocate memory for the decoder if needed.
+       if (next->coder == NULL) {
+               next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
+               if (next->coder == NULL)
+                       return LZMA_MEM_ERROR;
+
+               // End function is the same for encoder and decoder.
+               next->end = &delta_coder_end;
+               next->coder->next = LZMA_NEXT_CODER_INIT;
+       }
+
+       // Coding function is different for encoder and decoder.
+       next->code = code;
+
+       // Copy Uncompressed Size which is used to limit the output size
+       // in the Delta decoder.
+       next->coder->uncompressed_size = filters[0].uncompressed_size;
+
+       // Set the delta distance.
+       if (filters[0].options == NULL)
+               return LZMA_PROG_ERROR;
+       next->coder->distance = ((lzma_options_delta *)(filters[0].options))
+                       ->distance;
+       if (next->coder->distance < LZMA_DELTA_DISTANCE_MIN
+                       || next->coder->distance > LZMA_DELTA_DISTANCE_MAX)
+               return LZMA_HEADER_ERROR;
+
+       // Initialize the rest of the variables.
+       next->coder->pos = 0;
+       memzero(next->coder->history, LZMA_DELTA_DISTANCE_MAX);
+
+       // Initialize the next decoder in the chain, if any.
+       return lzma_next_filter_init(&next->coder->next,
+                               allocator, filters + 1);
+}
diff --git a/src/liblzma/common/delta_common.h b/src/liblzma/common/delta_common.h
new file mode 100644 (file)
index 0000000..3ec955b
--- /dev/null
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       delta_common.h
+/// \brief      Common stuff for Delta encoder and decoder
+//
+//  Copyright (C) 2007 Lasse Collin
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef LZMA_DELTA_COMMON_H
+#define LZMA_DELTA_COMMON_H
+
+#include "common.h"
+
+struct lzma_coder_s {
+       /// Next coder in the chain
+       lzma_next_coder next;
+
+       /// Uncompressed size - This is needed when we are the last
+       /// filter in the chain.
+       lzma_vli uncompressed_size;
+
+       /// Delta distance
+       size_t distance;
+
+       /// Position in history[]
+       uint8_t pos;
+
+       /// Buffer to hold history of the original data
+       uint8_t history[LZMA_DELTA_DISTANCE_MAX];
+};
+
+
+extern lzma_ret lzma_delta_coder_init(
+               lzma_next_coder *next, lzma_allocator *allocator,
+               const lzma_filter_info *filters, lzma_code_function code);
+
+#endif
diff --git a/src/liblzma/common/delta_decoder.c b/src/liblzma/common/delta_decoder.c
new file mode 100644 (file)
index 0000000..af2b840
--- /dev/null
@@ -0,0 +1,102 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       delta_decoder.c
+/// \brief      Delta filter decoder
+//
+//  Copyright (C) 2007, 2008 Lasse Collin
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "delta_decoder.h"
+#include "delta_common.h"
+
+
+/// Copies and decodes the data at the same time. This is used when Delta
+/// is the last filter in the chain.
+static void
+copy_and_decode(lzma_coder *coder,
+               const uint8_t *restrict in, uint8_t *restrict out, size_t size)
+{
+       const size_t distance = coder->distance;
+
+       for (size_t i = 0; i < size; ++i) {
+               out[i] = in[i] + coder->history[
+                               (distance + coder->pos) & 0xFF];
+               coder->history[coder->pos-- & 0xFF] = out[i];
+       }
+}
+
+
+/// Decodes the data in place. This is used when we are not the last filter
+/// in the chain.
+static void
+decode_in_place(lzma_coder *coder, uint8_t *buffer, size_t size)
+{
+       const size_t distance = coder->distance;
+
+       for (size_t i = 0; i < size; ++i) {
+               buffer[i] += coder->history[(distance + coder->pos) & 0xFF];
+               coder->history[coder->pos-- & 0xFF] = buffer[i];
+       }
+}
+
+
+
+static lzma_ret
+delta_decode(lzma_coder *coder, lzma_allocator *allocator,
+               const uint8_t *restrict in, size_t *restrict in_pos,
+               size_t in_size, uint8_t *restrict out,
+               size_t *restrict out_pos, size_t out_size, lzma_action action)
+{
+       lzma_ret ret;
+
+       if (coder->next.code == NULL) {
+               // Limit in_size so that we don't copy too much.
+               if ((lzma_vli)(in_size - *in_pos) > coder->uncompressed_size)
+                       in_size = *in_pos + (size_t)(coder->uncompressed_size);
+
+               const size_t in_avail = in_size - *in_pos;
+               const size_t out_avail = out_size - *out_pos;
+               const size_t size = MIN(in_avail, out_avail);
+
+               copy_and_decode(coder, in + *in_pos, out + *out_pos, size);
+
+               *in_pos += size;
+               *out_pos += size;
+
+               assert(coder->uncompressed_size <= LZMA_VLI_VALUE_MAX);
+               coder->uncompressed_size -= size;
+
+               ret = coder->uncompressed_size == 0
+                               ? LZMA_STREAM_END : LZMA_OK;
+
+       } else {
+               const size_t out_start = *out_pos;
+
+               ret = coder->next.code(coder->next.coder, allocator,
+                               in, in_pos, in_size, out, out_pos, out_size,
+                               action);
+
+               decode_in_place(coder, out + out_start, *out_pos - out_start);
+       }
+
+       return ret;
+}
+
+
+extern lzma_ret
+lzma_delta_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
+               const lzma_filter_info *filters)
+{
+       return lzma_delta_coder_init(next, allocator, filters, &delta_decode);
+}
similarity index 77%
rename from src/liblzma/common/delta_coder.h
rename to src/liblzma/common/delta_decoder.h
index 60cea95..bef9f58 100644 (file)
@@ -1,7 +1,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 //
-/// \file       delta_coder.h
-/// \brief      The Delta filter encoder and decoder
+/// \file       delta_decoder.h
+/// \brief      Delta filter decoder
 //
 //  Copyright (C) 2007 Lasse Collin
 //
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LZMA_DELTA_CODER_H
-#define LZMA_DELTA_CODER_H
+#ifndef LZMA_DELTA_DECODER_H
+#define LZMA_DELTA_DECODER_H
 
 #include "common.h"
 
-extern lzma_ret lzma_delta_encoder_init(lzma_next_coder *next,
-               lzma_allocator *allocator, const lzma_filter_info *filters);
-
 extern lzma_ret lzma_delta_decoder_init(lzma_next_coder *next,
                lzma_allocator *allocator, const lzma_filter_info *filters);
 
diff --git a/src/liblzma/common/delta_encoder.c b/src/liblzma/common/delta_encoder.c
new file mode 100644 (file)
index 0000000..b94f92d
--- /dev/null
@@ -0,0 +1,97 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       delta_encoder.c
+/// \brief      Delta filter encoder
+//
+//  Copyright (C) 2007, 2008 Lasse Collin
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "delta_encoder.h"
+#include "delta_common.h"
+
+
+/// Copies and encodes the data at the same time. This is used when Delta
+/// is the last filter in the chain.
+static void
+copy_and_encode(lzma_coder *coder,
+               const uint8_t *restrict in, uint8_t *restrict out, size_t size)
+{
+       const size_t distance = coder->distance;
+
+       for (size_t i = 0; i < size; ++i) {
+               const uint8_t tmp = coder->history[
+                               (distance + coder->pos) & 0xFF];
+               coder->history[coder->pos-- & 0xFF] = in[i];
+               out[i] = in[i] - tmp;
+       }
+}
+
+
+/// Encodes the data in place. This is used when we are not the last filter
+/// in the chain.
+static void
+encode_in_place(lzma_coder *coder, uint8_t *buffer, size_t size)
+{
+       const size_t distance = coder->distance;
+
+       for (size_t i = 0; i < size; ++i) {
+               const uint8_t tmp = coder->history[
+                               (distance + coder->pos) & 0xFF];
+               coder->history[coder->pos-- & 0xFF] = buffer[i];
+               buffer[i] -= tmp;
+       }
+}
+
+
+static lzma_ret
+delta_encode(lzma_coder *coder, lzma_allocator *allocator,
+               const uint8_t *restrict in, size_t *restrict in_pos,
+               size_t in_size, uint8_t *restrict out,
+               size_t *restrict out_pos, size_t out_size, lzma_action action)
+{
+       lzma_ret ret;
+
+       if (coder->next.code == NULL) {
+               const size_t in_avail = in_size - *in_pos;
+               const size_t out_avail = out_size - *out_pos;
+               const size_t size = MIN(in_avail, out_avail);
+
+               copy_and_encode(coder, in + *in_pos, out + *out_pos, size);
+
+               *in_pos += size;
+               *out_pos += size;
+
+               ret = action != LZMA_RUN && *in_pos == in_size
+                               ? LZMA_STREAM_END : LZMA_OK;
+
+       } else {
+               const size_t out_start = *out_pos;
+
+               ret = coder->next.code(coder->next.coder, allocator,
+                               in, in_pos, in_size, out, out_pos, out_size,
+                               action);
+
+               encode_in_place(coder, out + out_start, *out_pos - out_start);
+       }
+
+       return ret;
+}
+
+
+extern lzma_ret
+lzma_delta_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
+               const lzma_filter_info *filters)
+{
+       return lzma_delta_coder_init(next, allocator, filters, &delta_encode);
+}
diff --git a/src/liblzma/common/delta_encoder.h b/src/liblzma/common/delta_encoder.h
new file mode 100644 (file)
index 0000000..c669458
--- /dev/null
@@ -0,0 +1,28 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       delta_encoder.h
+/// \brief      Delta filter encoder
+//
+//  Copyright (C) 2007 Lasse Collin
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2.1 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef LZMA_DELTA_ENCODER_H
+#define LZMA_DELTA_ENCODER_H
+
+#include "common.h"
+
+extern lzma_ret lzma_delta_encoder_init(lzma_next_coder *next,
+               lzma_allocator *allocator, const lzma_filter_info *filters);
+
+#endif
index a11cb5c..03f1d84 100644 (file)
@@ -22,7 +22,7 @@
 #include "simple_coder.h"
 #include "subblock_decoder.h"
 #include "subblock_decoder_helper.h"
-#include "delta_coder.h"
+#include "delta_decoder.h"
 #include "lzma_decoder.h"
 #include "metadata_decoder.h"
 
index c2cd0a5..cd1f34d 100644 (file)
@@ -21,7 +21,7 @@
 #include "copy_coder.h"
 #include "simple_coder.h"
 #include "subblock_encoder.h"
-#include "delta_coder.h"
+#include "delta_encoder.h"
 #include "lzma_encoder.h"