Share LZMS x86 filter between compressor and decompressor
authorEric Biggers <ebiggers3@gmail.com>
Thu, 26 Dec 2013 02:57:17 +0000 (20:57 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 26 Dec 2013 02:57:17 +0000 (20:57 -0600)
Makefile.am
include/wimlib/lzms.h
src/lzms-common.c [new file with mode: 0644]
src/lzms-compress.c
src/lzms-decompress.c

index dbbdff6..2602100 100644 (file)
@@ -38,6 +38,7 @@ libwim_la_SOURCES =           \
        src/integrity.c         \
        src/join.c              \
        src/lookup_table.c      \
+       src/lzms-common.c       \
        src/lzms-compress.c     \
        src/lzms-decompress.c   \
        src/lz77.c              \
index de965fe..a18c526 100644 (file)
@@ -45,4 +45,9 @@
 #define LZMS_X86_MAX_GOOD_TARGET_OFFSET                65535
 #define LZMS_X86_MAX_TRANSLATION_OFFSET                1023
 
+#include <wimlib/types.h>
+
+extern void
+lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo);
+
 #endif /* _WIMLIB_LZMS_H  */
diff --git a/src/lzms-common.c b/src/lzms-common.c
new file mode 100644 (file)
index 0000000..946c655
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * lzms-common.c
+ *
+ * Code shared between the compressor and decompressor for the LZMS compression
+ * format.
+ */
+
+/*
+ * Copyright (C) 2013 Eric Biggers
+ *
+ * This file is part of wimlib, a library for working with WIM files.
+ *
+ * wimlib is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * wimlib 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 General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "wimlib/lzms.h"
+#include "wimlib/endianness.h"
+
+static s32
+lzms_maybe_do_x86_translation(u8 data[], s32 i, s32 num_op_bytes,
+                             s32 *closest_target_usage_p,
+                             s32 last_target_usages[], s32 max_trans_offset,
+                             bool undo)
+{
+       u16 pos;
+
+       if (undo) {
+               if (i - *closest_target_usage_p <= max_trans_offset) {
+                       LZMS_DEBUG("Undid x86 translation at position %d "
+                                  "(opcode 0x%02x)", i, data[i]);
+                       le32 *p32 = (le32*)&data[i + num_op_bytes];
+                       u32 n = le32_to_cpu(*p32);
+                       *p32 = cpu_to_le32(n - i);
+               }
+               pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]);
+       } else {
+               pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]);
+
+               if (i - *closest_target_usage_p <= max_trans_offset) {
+                       LZMS_DEBUG("Did x86 translation at position %d "
+                                  "(opcode 0x%02x)", i, data[i]);
+                       le32 *p32 = (le32*)&data[i + num_op_bytes];
+                       u32 n = le32_to_cpu(*p32);
+                       *p32 = cpu_to_le32(n + i);
+               }
+       }
+
+       i += num_op_bytes + sizeof(le32) - 1;
+
+       if (i - last_target_usages[pos] <= LZMS_X86_MAX_GOOD_TARGET_OFFSET)
+               *closest_target_usage_p = i;
+
+       last_target_usages[pos] = i;
+
+       return i + 1;
+}
+
+static s32
+lzms_may_x86_translate(const u8 p[], s32 *max_offset_ret)
+{
+       /* Switch on first byte of the opcode, assuming it is really an x86
+        * instruction.  */
+       *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET;
+       switch (p[0]) {
+       case 0x48:
+               if (p[1] == 0x8b) {
+                       if (p[2] == 0x5 || p[2] == 0xd) {
+                               /* Load relative (x86_64)  */
+                               return 3;
+                       }
+               } else if (p[1] == 0x8d) {
+                       if ((p[2] & 0x7) == 0x5) {
+                               /* Load effective address relative (x86_64)  */
+                               return 3;
+                       }
+               }
+               break;
+
+       case 0x4c:
+               if (p[1] == 0x8d) {
+                       if ((p[2] & 0x7) == 0x5) {
+                               /* Load effective address relative (x86_64)  */
+                               return 3;
+                       }
+               }
+               break;
+
+       case 0xe8:
+               /* Call relative  */
+               *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET / 2;
+               return 1;
+
+       case 0xe9:
+               /* Jump relative  */
+               *max_offset_ret = 0;
+               return 5;
+
+       case 0xf0:
+               if (p[1] == 0x83 && p[2] == 0x05) {
+                       /* Lock add relative  */
+                       return 3;
+               }
+               break;
+
+       case 0xff:
+               if (p[1] == 0x15) {
+                       /* Call indirect  */
+                       return 2;
+               }
+               break;
+       }
+       *max_offset_ret = 0;
+       return 1;
+}
+
+/*
+ * Translate relative addresses embedded in x86 instructions into absolute
+ * addresses (@undo == %false), or undo this translation (@undo == %true).
+ *
+ * @last_target_usages is a temporary array of length >= 65536.
+ */
+void
+lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo)
+{
+       s32 closest_target_usage = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1;
+
+       for (s32 i = 0; i < 65536; i++)
+               last_target_usages[i] = -LZMS_X86_MAX_GOOD_TARGET_OFFSET - 1;
+
+       for (s32 i = 0; i < size - 11; ) {
+               s32 max_trans_offset;
+               s32 n;
+
+               n = lzms_may_x86_translate(data + i, &max_trans_offset);
+               if (max_trans_offset) {
+                       i = lzms_maybe_do_x86_translation(data, i, n,
+                                                         &closest_target_usage,
+                                                         last_target_usages,
+                                                         max_trans_offset,
+                                                         undo);
+               } else {
+                       i += n;
+               }
+       }
+}
index e5abf24..3c34b52 100644 (file)
@@ -34,6 +34,7 @@
 #include "wimlib/assert.h"
 #include "wimlib/compressor_ops.h"
 #include "wimlib/compress_common.h"
+#include "wimlib/endianness.h"
 #include "wimlib/error.h"
 #include "wimlib/lzms.h"
 #include "wimlib/util.h"
@@ -44,17 +45,9 @@ struct lzms_compressor {
        u8 *window;
        u32 window_size;
        u32 max_block_size;
-
        s32 *last_target_usages;
 };
 
-static void
-lzms_preprocess_data(u8 *data, s32 size, s32 *last_target_usages)
-{
-       for (s32 i = 0; i < size - 11; i++) {
-       }
-}
-
 static size_t
 lzms_compress(const void *uncompressed_data, size_t uncompressed_size,
              void *compressed_data, size_t compressed_size_avail, void *_ctx)
@@ -71,8 +64,8 @@ lzms_compress(const void *uncompressed_data, size_t uncompressed_size,
        memcpy(ctx->window, uncompressed_data, uncompressed_size);
        ctx->window_size = uncompressed_size;
 
-       lzms_preprocess_data(ctx->window, ctx->window_size,
-                            ctx->last_target_usages);
+       lzms_x86_filter(ctx->window, ctx->window_size,
+                       ctx->last_target_usages, false);
 
        return 0;
 }
index 40d35bf..4d87e65 100644 (file)
@@ -1112,132 +1112,6 @@ lzms_decode_items(const u8 *cdata, size_t clen, u8 *ubuf, size_t ulen,
        return 0;
 }
 
-static s32
-lzms_try_x86_translation(u8 *ubuf, s32 i, s32 num_op_bytes,
-                        s32 *closest_target_usage_p, s32 last_target_usages[],
-                        s32 max_trans_offset)
-{
-       u16 pos;
-
-       if (i - *closest_target_usage_p <= max_trans_offset) {
-               LZMS_DEBUG("Performed x86 translation at position %d "
-                          "(opcode 0x%02x)", i, ubuf[i]);
-               le32 *p32 = (le32*)&ubuf[i + num_op_bytes];
-               u32 n = le32_to_cpu(*p32);
-               *p32 = cpu_to_le32(n - i);
-       }
-
-       pos = i + le16_to_cpu(*(const le16*)&ubuf[i + num_op_bytes]);
-
-       i += num_op_bytes + sizeof(le32) - 1;
-
-       if (i - last_target_usages[pos] <= LZMS_X86_MAX_GOOD_TARGET_OFFSET)
-               *closest_target_usage_p = i;
-
-       last_target_usages[pos] = i;
-
-       return i + 1;
-}
-
-static s32
-lzms_process_x86_translation(u8 *ubuf, s32 i, s32 *closest_target_usage_p,
-                            s32 last_target_usages[])
-{
-       /* Switch on first byte of the opcode, assuming it is really an x86
-        * instruction.  */
-       switch (ubuf[i]) {
-       case 0x48:
-               if (ubuf[i + 1] == 0x8b) {
-                       if (ubuf[i + 2] == 0x5 || ubuf[i + 2] == 0xd) {
-                               /* Load relative (x86_64)  */
-                               return lzms_try_x86_translation(ubuf, i, 3,
-                                                               closest_target_usage_p,
-                                                               last_target_usages,
-                                                               LZMS_X86_MAX_TRANSLATION_OFFSET);
-                       }
-               } else if (ubuf[i + 1] == 0x8d) {
-                       if ((ubuf[i + 2] & 0x7) == 0x5) {
-                               /* Load effective address relative (x86_64)  */
-                               return lzms_try_x86_translation(ubuf, i, 3,
-                                                               closest_target_usage_p,
-                                                               last_target_usages,
-                                                               LZMS_X86_MAX_TRANSLATION_OFFSET);
-                       }
-               }
-               break;
-
-       case 0x4c:
-               if (ubuf[i + 1] == 0x8d) {
-                       if ((ubuf[i + 2] & 0x7) == 0x5) {
-                               /* Load effective address relative (x86_64)  */
-                               return lzms_try_x86_translation(ubuf, i, 3,
-                                                               closest_target_usage_p,
-                                                               last_target_usages,
-                                                               LZMS_X86_MAX_TRANSLATION_OFFSET);
-                       }
-               }
-               break;
-
-       case 0xe8:
-               /* Call relative  */
-               return lzms_try_x86_translation(ubuf, i, 1, closest_target_usage_p,
-                                               last_target_usages,
-                                               LZMS_X86_MAX_TRANSLATION_OFFSET / 2);
-
-       case 0xe9:
-               /* Jump relative  */
-               return i + 5;
-
-       case 0xf0:
-               if (ubuf[i + 1] == 0x83 && ubuf[i + 2] == 0x05) {
-                       /* Lock add relative  */
-                       return lzms_try_x86_translation(ubuf, i, 3,
-                                                       closest_target_usage_p,
-                                                       last_target_usages,
-                                                       LZMS_X86_MAX_TRANSLATION_OFFSET);
-               }
-               break;
-
-       case 0xff:
-               if (ubuf[i + 1] == 0x15) {
-                       /* Call indirect  */
-                       return lzms_try_x86_translation(ubuf, i, 2,
-                                                       closest_target_usage_p,
-                                                       last_target_usages,
-                                                       LZMS_X86_MAX_TRANSLATION_OFFSET);
-               }
-               break;
-       }
-       return i + 1;
-}
-
-/* Postprocess the uncompressed data by undoing the translation of relative
- * addresses embedded in x86 instructions into absolute addresses.
- *
- * There does not appear to be any way to check to see if this postprocessing
- * actually needs to be done (or to plug in alternate filters, like in LZMA),
- * and the corresponding preprocessing seems to be done unconditionally.  */
-static void
-lzms_postprocess_data(u8 *ubuf, s32 ulen, s32 *last_target_usages)
-{
-       /* Offset (from beginning of buffer) of the most recent reference to a
-        * seemingly valid target address.  */
-       s32 closest_target_usage = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1;
-
-       /* Initialize the last_target_usages array.  Each entry will contain the
-        * offset (from beginning of buffer) of the most recently used target
-        * address beginning with two bytes equal to the array index.  */
-       for (s32 i = 0; i < 65536; i++)
-               last_target_usages[i] = -LZMS_X86_MAX_GOOD_TARGET_OFFSET - 1;
-
-       /* Check each byte in the buffer for an x86 opcode for which a
-        * translation may be possible.  No translations are done on any
-        * instructions starting in the last 11 bytes of the buffer.  */
-       for (s32 i = 0; i < ulen - 11; )
-               i = lzms_process_x86_translation(ubuf, i, &closest_target_usage,
-                                                last_target_usages);
-}
-
 static int
 lzms_decompress(const void *compressed_data, size_t compressed_size,
                void *uncompressed_data, size_t uncompressed_size, void *_ctx)
@@ -1281,8 +1155,8 @@ lzms_decompress(const void *compressed_data, size_t compressed_size,
                return -1;
 
        /* Postprocess the data.  */
-       lzms_postprocess_data(uncompressed_data, uncompressed_size,
-                             ctx->last_target_usages);
+       lzms_x86_filter(uncompressed_data, uncompressed_size,
+                       ctx->last_target_usages, true);
 
        LZMS_DEBUG("Decompression successful.");
        return 0;