]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Rework lzh and lzx decoding to improve its performance.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 3 Aug 2011 03:44:34 +0000 (23:44 -0400)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 3 Aug 2011 03:44:34 +0000 (23:44 -0400)
SVN-Revision: 3515

CMakeLists.txt
build/cmake/config.h.in
configure.ac
libarchive/archive_read_support_format_cab.c
libarchive/archive_read_support_format_lha.c

index c68b8d9e9efd5f99c933104aad02b2cd72d811ab..10dc5ffea6b178ceed2c42b80184ca54660d7a57 100644 (file)
@@ -238,6 +238,7 @@ LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H)
 LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H)
 LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H)
 LA_CHECK_INCLUDE_FILE("dlfcn.h" HAVE_DLFCN_H)
+LA_CHECK_INCLUDE_FILE("endian.h.h" HAVE_ENDIAN_H)
 LA_CHECK_INCLUDE_FILE("errno.h" HAVE_ERRNO_H)
 LA_CHECK_INCLUDE_FILE("ext2fs/ext2_fs.h" HAVE_EXT2FS_EXT2_FS_H)
 LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H)
@@ -264,6 +265,7 @@ LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H)
 LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H)
 LA_CHECK_INCLUDE_FILE("sys/acl.h" HAVE_SYS_ACL_H)
 LA_CHECK_INCLUDE_FILE("sys/cdefs.h" HAVE_SYS_CDEFS_H)
+LA_CHECK_INCLUDE_FILE("sys/endian.h.h" HAVE_SYS_ENDIAN_H)
 LA_CHECK_INCLUDE_FILE("sys/ioctl.h" HAVE_SYS_IOCTL_H)
 LA_CHECK_INCLUDE_FILE("sys/mkdev.h" HAVE_SYS_MKDEV_H)
 LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H)
index ff564c976a98c6d422bf7641d0db7075da8a5699..fb0d0ecb6419c9adc3911d8a6f4de5157b9b07bd 100644 (file)
@@ -382,6 +382,9 @@ typedef uint64_t uintmax_t;
 /* A possible errno value for invalid file format errors */
 #cmakedefine HAVE_EILSEQ 1
 
+/* Define to 1 if you have the <endian.h> header file. */
+#cmakedefine HAVE_ENDIAN_H 1
+
 /* Define to 1 if you have the <errno.h> header file. */
 #cmakedefine HAVE_ERRNO_H 1
 
@@ -808,6 +811,9 @@ typedef uint64_t uintmax_t;
 /* Define to 1 if you have the <sys/ea.h> header file. */
 #cmakedefine HAVE_SYS_EA_H 1
 
+/* Define to 1 if you have the <sys/endian.h> header file. */
+#cmakedefine HAVE_SYS_ENDIAN_H 1
+
 /* Define to 1 if you have the <sys/extattr.h> header file. */
 #cmakedefine HAVE_SYS_EXTATTR_H 1
 
index 33268b7fab1bab1c7d323ff3dda8cc47f3695adf..ff60764d2ce73e8068e9d0395d2ef6ce8c8cc26e 100644 (file)
@@ -201,13 +201,13 @@ esac
 # Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h copyfile.h ctype.h errno.h])
-AC_CHECK_HEADERS([ext2fs/ext2_fs.h fcntl.h grp.h])
+AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h copyfile.h ctype.h endian.h])
+AC_CHECK_HEADERS([errno.h ext2fs/ext2_fs.h fcntl.h grp.h])
 AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h])
 AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h])
 AC_CHECK_HEADERS([locale.h paths.h poll.h pwd.h regex.h signal.h stdarg.h])
 AC_CHECK_HEADERS([stdint.h stdlib.h string.h])
-AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/extattr.h sys/ioctl.h])
+AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/endian.h sys/extattr.h sys/ioctl.h])
 AC_CHECK_HEADERS([sys/mkdev.h sys/mount.h])
 AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/select.h sys/statfs.h sys/statvfs.h])
 AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h])
index 87cb47b3e239daccf391ed6e702aeff675601e56..6ed59a24962d80679583e4d6c0e582d8f16219d5 100644 (file)
@@ -82,14 +82,14 @@ struct lzx_dec {
        size_t                   block_size;
        size_t                   block_bytes_avail;
        /* Repeated offset. */
-       uint32_t                 r0, r1, r2;
+       int                      r0, r1, r2;
        unsigned char            rbytes[4];
        int                      rbytes_avail;
        int                      length_header;
        int                      position_slot;
        int                      offset_bits;
 
-       struct pos_tbl {
+       struct lzx_pos_tbl {
                int              base;
                int              footer_bits;
        }                       *pos_tbl;
@@ -341,6 +341,8 @@ static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,
 static int64_t cab_consume_cfdata(struct archive_read *, int64_t);
 static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t);
 static int     lzx_decode_init(struct lzx_stream *, int);
+static int     lzx_read_blocks(struct lzx_stream *, int);
+static int     lzx_decode_blocks(struct lzx_stream *, int);
 static void    lzx_decode_free(struct lzx_stream *);
 static void    lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
 static void    lzx_cleanup_bitstream(struct lzx_stream *);
@@ -1049,6 +1051,14 @@ archive_read_format_cab_read_data(struct archive_read *a,
 static uint32_t
 cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
 {
+/*
+ * x86 proccessor family can read misaligned data without an access error.
+ */
+#ifdef __i386__
+#  define lzx_le32dec(a)       (*(const uint32_t *)(a))
+#else
+#  define lzx_le32dec(a)       archive_le132ec(a)
+#endif
        const unsigned char *b;
        int u32num;
        uint32_t sum;
@@ -1057,10 +1067,11 @@ cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
        sum = seed;
        b = p;
        while (--u32num >= 0) {
-               sum ^= archive_le32dec(b);
+               sum ^= lzx_le32dec(b);
                b += 4;
        }
        return (sum);
+#undef lzx_le32dec
 }
 
 static uint32_t
@@ -2213,7 +2224,7 @@ lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
  *  False : we met that strm->next_in is empty, we have to get following
  *          bytes. */
 #define lzx_br_read_ahead(strm, br, n) \
-       (lzx_br_has((br), (n)) || lzx_br_fillup(strm))
+       (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
 /*  True  : the cache buffer has some bits as much as we need.
  *  False : there are no enough bits in the cache buffer to be used,
  *          we have to get following bytes if we could. */
@@ -2240,79 +2251,93 @@ static const uint16_t cache_masks[] = {
  * Returns 0 if the cache buffer is not full; input buffer is empty.
  */
 static int
-lzx_br_fillup(struct lzx_stream *strm)
+lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
 {
-       struct lzx_dec *ds = strm->ds;
-       int n = CACHE_BITS - ds->br.cache_avail;
+/*
+ * x86 proccessor family can read misaligned data without an access error.
+ */
+#ifdef __i386__
+#  define lzx_le16dec(a)       (*(const uint16_t *)(a))
+#else
+#  define lzx_le16dec(a)       archive_le16dec(a)
+#endif
+       int n = CACHE_BITS - br->cache_avail;
 
-       if (ds->br.have_odd && n >= 16 && strm->avail_in > 0) {
-               ds->br.cache_buffer =
-                  (ds->br.cache_buffer << 16) |
-                  ((uint16_t)(*strm->next_in)) << 8 |
-                   ds->br.odd;
-               strm->next_in++;
-               strm->avail_in--;
-               strm->total_in++;
-               ds->br.cache_avail += 16;
-               n -= 16;
-               ds->br.have_odd = 0;
-       }
-       while (n >= 16) {
-               if (strm->avail_in < 2) {
-                       /* There is not enough compressed data to fill up the
-                        * cache buffer. */
-                       if (strm->avail_in == 1) {
-                               ds->br.odd = *strm->next_in++;
-                               strm->avail_in--;
-                               strm->total_in++;
-                               ds->br.have_odd = 1;
-                       }
-                       return (0);
-               }
-               if (strm->avail_in >= 8) {
-                       int words;
-                       switch (words = (n >> 4)) {
-                       case 4:
-                               ds->br.cache_buffer =
-                                   (((uint64_t)archive_le16dec(
+       for (;;) {
+               switch (n >> 4) {
+               case 4:
+                       if (strm->avail_in >= 8) {
+                               br->cache_buffer =
+                                   (((uint64_t)lzx_le16dec(
                                        strm->next_in)) << 48) |
-                                   (((uint64_t)archive_le16dec(
+                                   (((uint64_t)lzx_le16dec(
                                        strm->next_in+2)) << 32) |
-                                   (((uint32_t)archive_le16dec(
+                                   (((uint32_t)lzx_le16dec(
                                        strm->next_in+4)) << 16) |
-                                   archive_le16dec(strm->next_in+6);
-                               break;
-                       case 3:
-                               ds->br.cache_buffer =
-                                  (ds->br.cache_buffer << 48) |
-                                   (((uint64_t)archive_le16dec(
+                                   lzx_le16dec(strm->next_in+6);
+                               strm->next_in += 8;
+                               strm->avail_in -= 8;
+                               br->cache_avail += 8 * 8;
+                               return (1);
+                       }
+                       break;
+               case 3:
+                       if (strm->avail_in >= 6) {
+                               br->cache_buffer =
+                                  (br->cache_buffer << 48) |
+                                   (((uint64_t)lzx_le16dec(
                                        strm->next_in)) << 32) |
-                                   (((uint32_t)archive_le16dec(
+                                   (((uint32_t)lzx_le16dec(
                                        strm->next_in+2)) << 16) |
-                                   archive_le16dec(strm->next_in+4);
-                               break;
-                       default:
-                               words = 0;
-                               break;
-                       }
-                       if (words) {
-                               strm->next_in += words << 1;
-                               strm->avail_in -= words << 1;
-                               strm->total_in += words << 1;
-                               ds->br.cache_avail += words << 4;
+                                   lzx_le16dec(strm->next_in+4);
+                               strm->next_in += 6;
+                               strm->avail_in -= 6;
+                               br->cache_avail += 6 * 8;
                                return (1);
                        }
+                       break;
+               case 0:
+                       /* We have enough compressed data in
+                        * the cache buffer.*/
+                       return (1);
+               default:
+                       break;
                }
-               ds->br.cache_buffer =
-                  (ds->br.cache_buffer << 16) |
-                   archive_le16dec(strm->next_in);
+               if (strm->avail_in < 2) {
+                       /* There is not enough compressed data to
+                        * fill up the cache buffer. */
+                       if (strm->avail_in == 1) {
+                               br->odd = *strm->next_in++;
+                               strm->avail_in--;
+                               br->have_odd = 1;
+                       }
+                       return (0);
+               }
+               br->cache_buffer =
+                  (br->cache_buffer << 16) |
+                   lzx_le16dec(strm->next_in);
                strm->next_in += 2;
                strm->avail_in -= 2;
-               strm->total_in += 2;
-               ds->br.cache_avail += 16;
+               br->cache_avail += 16;
                n -= 16;
        }
-       return (1);
+#undef lzx_le16dec
+}
+
+static void
+lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
+{
+       int n = CACHE_BITS - br->cache_avail;
+
+       if (br->have_odd && n >= 16 && strm->avail_in > 0) {
+               br->cache_buffer =
+                  (br->cache_buffer << 16) |
+                  ((uint16_t)(*strm->next_in)) << 8 | br->odd;
+               strm->next_in++;
+               strm->avail_in--;
+               br->cache_avail += 16;
+               br->have_odd = 0;
+       }
 }
 
 static void
@@ -2331,13 +2356,6 @@ lzx_cleanup_bitstream(struct lzx_stream *strm)
  * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
  *    is broken or you do not set 'last' flag properly.
  */
-static int
-lzx_decode(struct lzx_stream *strm, int last)
-{
-       struct lzx_dec *ds = strm->ds;
-       struct lzx_br *br = &(ds->br);
-       int c, i, r;
-       unsigned rbits;
 #define ST_RD_TRANSLATION      0
 #define ST_RD_TRANSLATION_SIZE 1
 #define ST_RD_BLOCK_TYPE       2
@@ -2361,9 +2379,40 @@ lzx_decode(struct lzx_stream *strm, int last)
 #define ST_REAL_POS            20
 #define ST_COPY                        21
 
+static int
+lzx_decode(struct lzx_stream *strm, int last)
+{
+       struct lzx_dec *ds = strm->ds;
+       int64_t avail_in;
+       int r;
+
        if (ds->error)
                return (ds->error);
 
+       avail_in = strm->avail_in;
+       lzx_br_fixup(strm, &(ds->br));
+       do {
+               if (ds->state < ST_MAIN)
+                       r = lzx_read_blocks(strm, last);
+               else {
+                       int64_t bytes_written = strm->avail_out;
+                       r = lzx_decode_blocks(strm, last);
+                       bytes_written -= strm->avail_out;
+                       strm->next_out += bytes_written;
+                       strm->total_out += bytes_written;
+               }
+       } while (r == 100);
+       strm->total_in += avail_in - strm->avail_in;
+       return (r);
+}
+
+static int
+lzx_read_blocks(struct lzx_stream *strm, int last)
+{
+       struct lzx_dec *ds = strm->ds;
+       struct lzx_br *br = &(ds->br);
+       int i, r;
+
        for (;;) {
                switch (ds->state) {
                case ST_RD_TRANSLATION:
@@ -2478,16 +2527,21 @@ lzx_decode(struct lzx_stream *strm, int last)
                                        ds->rbytes[ds->rbytes_avail++] =
                                            *strm->next_in++;
                                        strm->avail_in--;
-                                       strm->total_in++;
                                }
                                if (ds->state == ST_RD_R0) {
                                        ds->r0 = archive_le32dec(ds->rbytes);
+                                       if (ds->r0 < 0)
+                                               goto failed;
                                        ds->state = ST_RD_R1;
                                } else if (ds->state == ST_RD_R1) {
                                        ds->r1 = archive_le32dec(ds->rbytes);
+                                       if (ds->r1 < 0)
+                                               goto failed;
                                        ds->state = ST_RD_R2;
                                } else if (ds->state == ST_RD_R2) {
                                        ds->r2 = archive_le32dec(ds->rbytes);
+                                       if (ds->r2 < 0)
+                                               goto failed;
                                        /* We've gotten all repeated offsets. */
                                        ds->state = ST_COPY_UNCOMP1;
                                }
@@ -2526,7 +2580,6 @@ lzx_decode(struct lzx_stream *strm, int last)
                                strm->avail_out -= ll;
                                strm->total_out += ll;
                                strm->avail_in -= ll;
-                               strm->total_in += ll;
                                ds->w_pos = (ds->w_pos + ll) & ds->w_mask;
                                ds->block_bytes_avail -= ll;
                        }
@@ -2543,7 +2596,6 @@ lzx_decode(struct lzx_stream *strm, int last)
                                }
                                strm->next_in++;
                                strm->avail_in --;
-                               strm->total_in ++;
                        }
                        /* This block ended. */
                        ds->state = ST_RD_BLOCK_TYPE;
@@ -2661,37 +2713,80 @@ lzx_decode(struct lzx_stream *strm, int last)
                        if (!lzx_make_huffman_table(&ds->lt))
                                goto failed;
                        ds->state = ST_MAIN;
-                       /* FALL THROUGH */
+                       return (100);
+               }
+       }
+failed:
+       return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzx_decode_blocks(struct lzx_stream *strm, int last)
+{
+       struct lzx_dec *ds = strm->ds;
+       struct lzx_br bre = ds->br;
+       struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);
+       const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
+       unsigned char *outp = strm->next_out;
+       unsigned char *endp = outp + strm->avail_out;
+       unsigned char *w_buff = ds->w_buff;
+       unsigned char *at_bitlen = at->bitlen;
+       unsigned char *lt_bitlen = lt->bitlen;
+       unsigned char *mt_bitlen = mt->bitlen;
+       size_t block_bytes_avail = ds->block_bytes_avail;
+       int at_max_bits = at->max_bits;
+       int lt_max_bits = lt->max_bits;
+       int mt_max_bits = mt->max_bits;
+       int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+       int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+       int length_header = ds->length_header;
+       int offset_bits = ds->offset_bits;
+       int position_slot = ds->position_slot;
+       int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+       int state = ds->state;
+       char block_type = ds->block_type;
+
+       for (;;) {
+               switch (state) {
                case ST_MAIN:
                        for (;;) {
-                               if (ds->block_bytes_avail == 0) {
+                               if (block_bytes_avail == 0) {
                                        /* This block ended. */
                                        ds->state = ST_RD_BLOCK_TYPE;
+                                       ds->br = bre;
+                                       ds->block_bytes_avail =
+                                           block_bytes_avail;
+                                       ds->copy_len = copy_len;
+                                       ds->copy_pos = copy_pos;
+                                       ds->length_header = length_header;
+                                       ds->position_slot = position_slot;
+                                       ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+                                       ds->w_pos = w_pos;
+                                       strm->avail_out = endp - outp;
                                        return (ARCHIVE_EOF);
                                }
-                               if (strm->avail_out <= 0)
+                               if (outp >= endp)
                                        /* Output buffer is empty. */
-                                       return (ARCHIVE_OK);
+                                       goto next_data;
 
-                               if (!lzx_br_ensure(strm, br, ds->mt.max_bits)) {
+                               if (!lzx_br_ensure(strm, &bre, mt_max_bits)) {
                                        if (!last)
-                                               return (ARCHIVE_OK);
+                                               goto next_data;
                                        /* Remaining bits are less than
                                         * maximum bits(mt.max_bits) but maybe
                                         * it still remains as much as we need,
                                         * so we should try to use it with
                                         * dummy bits. */
-                                       rbits = lzx_br_bits_forced(
-                                           br, ds->mt.max_bits);
-                                       c = lzx_decode_huffman(&(ds->mt),rbits);
-                                       lzx_br_consume(br, ds->mt.bitlen[c]);
-                                       if (!lzx_br_has(br, 0))
+                                       c = lzx_decode_huffman(mt,
+                                             lzx_br_bits_forced(
+                                               &bre, mt_max_bits));
+                                       lzx_br_consume(&bre, mt_bitlen[c]);
+                                       if (!lzx_br_has(&bre, 0))
                                                goto failed;/* Over read. */
                                } else {
-                                       rbits = lzx_br_bits(br,
-                                           ds->mt.max_bits);
-                                       c = lzx_decode_huffman(&(ds->mt),rbits);
-                                       lzx_br_consume(br, ds->mt.bitlen[c]);
+                                       c = lzx_decode_huffman(mt,
+                                             lzx_br_bits(&bre, mt_max_bits));
+                                       lzx_br_consume(&bre, mt_bitlen[c]);
                                }
                                if (c > UCHAR_MAX)
                                        break;
@@ -2700,72 +2795,70 @@ lzx_decode(struct lzx_stream *strm, int last)
                                 */
                                /* Save a decoded code to reference it
                                 * afterward. */
-                               ds->w_buff[ds->w_pos] = c;
-                               ds->w_pos = (ds->w_pos + 1) & ds->w_mask;
+                               w_buff[w_pos] = c;
+                               w_pos = (w_pos + 1) & w_mask;
                                /* Store the decoded code to output buffer. */
-                               *strm->next_out++ = c;
-                               strm->avail_out--;
-                               strm->total_out++;
-                               ds->block_bytes_avail--;
+                               *outp++ = c;
+                               block_bytes_avail--;
                        }
                        /*
                         * Get a match code, its length and offset.
                         */
                        c -= UCHAR_MAX + 1;
-                       ds->length_header = c & 7;
-                       ds->position_slot = c >> 3;
+                       length_header = c & 7;
+                       position_slot = c >> 3;
                        /* FALL THROUGH */
                case ST_LENGTH:
                        /*
                         * Get a length.
                         */
-                       if (ds->length_header == 7) {
-                               if (!lzx_br_ensure(strm, br, ds->lt.max_bits)) {
+                       if (length_header == 7) {
+                               if (!lzx_br_ensure(strm, &bre, lt_max_bits)) {
                                        if (!last) {
-                                               ds->state = ST_LENGTH;
-                                               return (ARCHIVE_OK);
+                                               state = ST_LENGTH;
+                                               goto next_data;
                                        }
-                                       rbits = lzx_br_bits_forced(
-                                           br, ds->lt.max_bits);
-                                       c = lzx_decode_huffman(&(ds->lt),rbits);
-                                       lzx_br_consume(br, ds->lt.bitlen[c]);
-                                       if (!lzx_br_has(br, 0))
+                                       c = lzx_decode_huffman(lt,
+                                             lzx_br_bits_forced(
+                                               &bre, lt_max_bits));
+                                       lzx_br_consume(&bre, lt_bitlen[c]);
+                                       if (!lzx_br_has(&bre, 0))
                                                goto failed;/* Over read. */
                                } else {
-                                       rbits = lzx_br_bits(br,
-                                           ds->lt.max_bits);
-                                       c = lzx_decode_huffman(&(ds->lt),rbits);
-                                       lzx_br_consume(br, ds->lt.bitlen[c]);
+                                       c = lzx_decode_huffman(lt,
+                                           lzx_br_bits(&bre, lt_max_bits));
+                                       lzx_br_consume(&bre, lt_bitlen[c]);
                                }
-                               ds->copy_len = c + 7 + 2;
+                               copy_len = c + 7 + 2;
                        } else
-                               ds->copy_len = ds->length_header + 2;
+                               copy_len = length_header + 2;
+                       if ((size_t)copy_len > block_bytes_avail)
+                               goto failed;
                        /*
                         * Get an offset.
                         */
-                       switch (ds->position_slot) {
+                       switch (position_slot) {
                        case 0: /* Use repeated offset 0. */
-                               ds->copy_pos = ds->r0;
-                               break;
+                               copy_pos = r0;
+                               state = ST_REAL_POS;
+                               continue;
                        case 1: /* Use repeated offset 1. */
-                               ds->copy_pos = ds->r1;
+                               copy_pos = r1;
                                /* Swap repeated offset. */
-                               ds->r1 = ds->r0;
-                               ds->r0 = ds->copy_pos;
-                               break;
+                               r1 = r0;
+                               r0 = copy_pos;
+                               state = ST_REAL_POS;
+                               continue;
                        case 2: /* Use repeated offset 2. */
-                               ds->copy_pos = ds->r2;
+                               copy_pos = r2;
                                /* Swap repeated offset. */
-                               ds->r2 = ds->r0;
-                               ds->r0 = ds->copy_pos;
-                               break;
+                               r2 = r0;
+                               r0 = copy_pos;
+                               state = ST_REAL_POS;
+                               continue;
                        default:
-                               ds->offset_bits =
-                                   ds->pos_tbl[ds->position_slot].footer_bits;
-                               break;
-                       }
-                       if (ds->position_slot <= 2) {
-                               ds->state = ST_REAL_POS;
+                               offset_bits =
+                                   pos_tbl[position_slot].footer_bits;
                                break;
                        }
                        /* FALL THROUGH */
@@ -2774,110 +2867,128 @@ lzx_decode(struct lzx_stream *strm, int last)
                         * Get the offset, which is a distance from
                         * current window position.
                         */
-                       if (ds->block_type == ALIGNED_OFFSET_BLOCK &&
-                           ds->offset_bits >= 3) {
-                               int offbits = ds->offset_bits - 3;
+                       if (block_type == ALIGNED_OFFSET_BLOCK &&
+                           offset_bits >= 3) {
+                               int offbits = offset_bits - 3;
 
-                               if (!lzx_br_ensure(strm, br, offbits)) {
-                                       ds->state = ST_OFFSET;
+                               if (!lzx_br_ensure(strm, &bre, offbits)) {
+                                       state = ST_OFFSET;
                                        if (last)
                                                goto failed;
-                                       return (ARCHIVE_OK);
+                                       goto next_data;
                                }
-                               c = lzx_br_bits(br, offbits);
-                               ds->copy_pos = c << 3;
+                               copy_pos = lzx_br_bits(&bre, offbits) << 3;
 
                                /* Get an aligned number. */
-                               if (!lzx_br_ensure(strm, br,
-                                   offbits + ds->at.max_bits)) {
+                               if (!lzx_br_ensure(strm, &bre,
+                                   offbits + at_max_bits)) {
                                        if (!last) {
-                                               ds->state = ST_OFFSET;
-                                               return (ARCHIVE_OK);
+                                               state = ST_OFFSET;
+                                               goto next_data;
                                        }
-                                       lzx_br_consume(br, offbits);
-                                       rbits = lzx_br_bits_forced(br,
-                                           ds->at.max_bits);
-                                       c = lzx_decode_huffman(&(ds->at),rbits);
-                                       lzx_br_consume(br, ds->at.bitlen[c]);
-                                       if (!lzx_br_has(br, 0))
+                                       lzx_br_consume(&bre, offbits);
+                                       c = lzx_decode_huffman(at,
+                                             lzx_br_bits_forced(&bre,
+                                               at_max_bits));
+                                       lzx_br_consume(&bre, at_bitlen[c]);
+                                       if (!lzx_br_has(&bre, 0))
                                                goto failed;/* Over read. */
                                } else {
-                                       lzx_br_consume(br, offbits);
-                                       rbits = lzx_br_bits(br,
-                                           ds->at.max_bits);
-                                       c = lzx_decode_huffman(&(ds->at),rbits);
-                                       lzx_br_consume(br, ds->at.bitlen[c]);
+                                       lzx_br_consume(&bre, offbits);
+                                       c = lzx_decode_huffman(at,
+                                             lzx_br_bits(&bre, at_max_bits));
+                                       lzx_br_consume(&bre, at_bitlen[c]);
                                }
                                /* Add an aligned number. */
-                               ds->copy_pos += c;
+                               copy_pos += c;
                        } else {
-                               if (!lzx_br_ensure(strm, br, ds->offset_bits)) {
-                                       ds->state = ST_OFFSET;
+                               if (!lzx_br_ensure(strm, &bre, offset_bits)) {
+                                       state = ST_OFFSET;
                                        if (last)
                                                goto failed;
-                                       return (ARCHIVE_OK);
+                                       goto next_data;
                                }
-                               c = lzx_br_bits(br, ds->offset_bits);
-                               lzx_br_consume(br, ds->offset_bits);
-                               ds->copy_pos = c;
+                               copy_pos = lzx_br_bits(&bre, offset_bits);
+                               lzx_br_consume(&bre, offset_bits);
                        }
-                       ds->copy_pos += ds->pos_tbl[ds->position_slot].base -2;
+                       copy_pos += pos_tbl[position_slot].base -2;
 
                        /* Update repeated offset LRU queue. */
-                       ds->r2 = ds->r1;
-                       ds->r1 = ds->r0;
-                       ds->r0 = ds->copy_pos;
+                       r2 = r1;
+                       r1 = r0;
+                       r0 = copy_pos;
                        /* FALL THROUGH */
                case ST_REAL_POS:
                        /*
                         * Compute a real position in window.
                         */
-                       ds->copy_pos = (ds->w_pos - ds->copy_pos) & ds->w_mask;
+                       copy_pos = (w_pos - copy_pos) & w_mask;
                        /* FALL THROUGH */
                case ST_COPY:
                        /*
                         * Copy several bytes as extracted data from the window
                         * into the output buffer.
                         */
-                       if ((size_t)ds->copy_len > ds->block_bytes_avail)
-                               goto failed;
-                       do {
+                       for (;;) {
                                const unsigned char *s;
-                               unsigned char *d;
-                               int l,ll;
-                               if (strm->avail_out <= 0) {
-                                       /* Output buffer is empty. */
-                                       ds->state = ST_COPY;
-                                       return (ARCHIVE_OK);
+                               int l;
+
+                               l = copy_len;
+                               if (copy_pos > w_pos) {
+                                       if (l > w_size - copy_pos)
+                                               l = w_size - copy_pos;
+                               } else {
+                                       if (l > w_size - w_pos)
+                                               l = w_size - w_pos;
                                }
-                               l = ds->copy_len;
-                               if (l > ds->w_size - ds->copy_pos)
-                                       l = ds->w_size - ds->copy_pos;
-                               if (l > ds->w_size - ds->w_pos)
-                                       l = ds->w_size - ds->w_pos;
-                               if (l > strm->avail_out)
-                                       l = (int)strm->avail_out;
-                               ll = l;
-                               s = &(ds->w_buff[ds->copy_pos]);
-                               d = &(ds->w_buff[ds->w_pos]);
-                               while (--l >= 0) {
-                                       *strm->next_out++ = *s;
-                                       *d++ = *s++;
+                               if (outp + l >= endp)
+                                       l = endp - outp;
+                               s = w_buff + copy_pos;
+                               if (l >= 8 && ((copy_pos + l < w_pos)
+                                 || (w_pos + l < copy_pos))) {
+                                       memcpy(w_buff + w_pos, s, l);
+                                       memcpy(outp, s, l);
+                               } else {
+                                       unsigned char *d;
+                                       int li;
+
+                                       d = w_buff + w_pos;
+                                       for (li = 0; li < l; li++)
+                                               outp[li] = d[li] = s[li];
                                }
-                               strm->avail_out -= ll;
-                               strm->total_out += ll;
-                               ds->copy_pos =
-                                   (ds->copy_pos + ll) & ds->w_mask;
-                               ds->w_pos = (ds->w_pos + ll) & ds->w_mask;
-                               ds->copy_len -= ll;
-                               ds->block_bytes_avail -= ll;
-                       } while (ds->copy_len > 0);
-                       ds->state = ST_MAIN;
+                               outp += l;
+                               copy_pos = (copy_pos + l) & w_mask;
+                               w_pos = (w_pos + l) & w_mask;
+                               block_bytes_avail -= l;
+                               if (copy_len <= l)
+                                       /* A copy of current pattern ended. */
+                                       break;
+                               copy_len -= l;
+                               if (outp >= endp) {
+                                       /* Output buffer is empty. */
+                                       state = ST_COPY;
+                                       goto next_data;
+                               }
+                       }
+                       state = ST_MAIN;
                        break;
                }
        }
 failed:
        return (ds->error = ARCHIVE_FAILED);
+next_data:
+       ds->br = bre;
+       ds->block_bytes_avail = block_bytes_avail;
+       ds->copy_len = copy_len;
+       ds->copy_pos = copy_pos;
+       ds->length_header = length_header;
+       ds->offset_bits = offset_bits;
+       ds->position_slot = position_slot;
+       ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+       ds->state = state;
+       ds->w_pos = w_pos;
+       strm->avail_out = endp - outp;
+       return (ARCHIVE_OK);
 }
 
 static int
index 79826efca27816c4427f8b79f4a0a2eb26704237..6bbdf93d91ebe852a61718c0ab650928f3caeedb 100644 (file)
 
 #include "archive_platform.h"
 
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -284,7 +290,7 @@ static int  lzh_decode_init(struct lzh_stream *, const char *);
 static void    lzh_decode_free(struct lzh_stream *);
 static uint16_t lzh_crc16(struct lzh_stream *);
 static int     lzh_decode(struct lzh_stream *, int);
-static int     lzh_br_fillup(struct lzh_stream *);
+static int     lzh_br_fillup(struct lzh_stream *, struct lzh_br *);
 static int     lzh_huffman_init(struct huffman *, size_t, int);
 static void    lzh_huffman_free(struct huffman *);
 static int     lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);
@@ -1694,15 +1700,38 @@ lha_crc16(uint16_t crc, const void *pp, size_t len)
 {
        const unsigned char *buff = (const unsigned char *)pp;
 
-       while (len >= 4) {
+       while (len >= 8) {
+               CRC16(crc, *buff++); CRC16(crc, *buff++);
+               CRC16(crc, *buff++); CRC16(crc, *buff++);
+               CRC16(crc, *buff++); CRC16(crc, *buff++);
+               CRC16(crc, *buff++); CRC16(crc, *buff++);
+               len -= 8;
+       }
+       switch (len) {
+       case 7:
                CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 6:
                CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 5:
                CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 4:
                CRC16(crc, *buff++);
-               len -= 4;
-       }
-       while (len-- > 0)
+               /* FALL THROUGH */
+       case 3:
+               CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 2:
                CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 1:
+               CRC16(crc, *buff++);
+               /* FALL THROUGH */
+       case 0:
+               break;
+       }
        return (crc);
 }
 
@@ -1820,9 +1849,10 @@ lzh_crc16(struct lzh_stream *strm)
  *  False : we met that strm->next_in is empty, we have to get following
  *          bytes. */
 #define lzh_br_read_ahead(strm, br, n) \
-       (lzh_br_has(br, (n)) || lzh_br_fillup(strm))
+       (lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))
 /* Notify how man bits we consumed. */
 #define lzh_br_consume(br, n)  ((br)->cache_avail -= (n))
+#define lzh_br_unconsume(br, n)        ((br)->cache_avail += (n))
 
 static const uint16_t cache_masks[] = {
        0x0000, 0x0001, 0x0003, 0x0007,
@@ -1840,55 +1870,103 @@ static const uint16_t cache_masks[] = {
  * Returns 0 if the cache buffer is not full; input buffer is empty.
  */
 static int
-lzh_br_fillup(struct lzh_stream *strm)
+lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
 {
-       struct lzh_dec *ds = strm->ds;
-       int n = CACHE_BITS - ds->br.cache_avail;
+/*
+ * x86 proccessor family can read misaligned data without an access error.
+ */
+#if defined(__i386__)
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define lzh_be16dec(p) _byteswap_ushort(*(const uint16_t *)(p))
+#  elif defined(be16toh)
+#    define lzh_be16dec(p) be16toh(*(const uint16_t *)(p))
+#  elif defined(betoh16)
+#    define lzh_be16dec(p) betoh16(*(const uint16_t *)(p))
+#  else
+#    define lzh_be16dec        archive_be16dec
+#  endif
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define lzh_be16dec(p) _byteswap_ulong(*(const uint32_t *)(p))
+#  elif defined(be32toh)
+#    define lzh_be32dec(p) be32toh(*(const uint32_t *)(p))
+#  elif defined(betoh32)
+#    define lzh_be32dec(p) betoh32(*(const uint32_t *)(p))
+#  else
+#    define lzh_be32dec        archive_be32dec
+#  endif
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define lzh_be16dec(p) _byteswap_uint64(*(const uint64_t *)(p))
+#  elif defined(be64toh)
+#    define lzh_be64dec(p) be64toh(*(const uint64_t *)(p))
+#  elif defined(betoh64)
+#    define lzh_be64dec(p) betoh64(*(const uint64_t *)(p))
+#  else
+#    define lzh_be64dec        archive_be64dec
+#  endif
+#else
+#  define lzh_be16dec  archive_be16dec
+#  define lzh_be32dec  archive_be32dec
+#  define lzh_be64dec  archive_be64dec
+#endif
+       int n = CACHE_BITS - br->cache_avail;
 
-       while (n >= 8) {
+       for (;;) {
+               switch (n >> 3) {
+               case 8:
+                       if (strm->avail_in >= 8) {
+                               br->cache_buffer =
+                                   lzh_be64dec(strm->next_in);
+                               strm->next_in += 8;
+                               strm->avail_in -= 8;
+                               br->cache_avail += 8 * 8;
+                               return (1);
+                       }
+                       break;
+               case 7:
+                       if (strm->avail_in >= 8) {/* Read extra one. */
+                               br->cache_buffer =
+                                  (br->cache_buffer << 56) |
+                                   lzh_be64dec(strm->next_in) >> 8;
+                               strm->next_in += 7;
+                               strm->avail_in -= 7;
+                               br->cache_avail += 7 * 8;
+                               return (1);
+                       }
+                       break;
+               case 6:
+                       if (strm->avail_in >= 6) {
+                               br->cache_buffer =
+                                  (br->cache_buffer << 48) |
+                                  (((uint64_t)lzh_be32dec(
+                                      strm->next_in)) << 16) |
+                                   lzh_be16dec(&strm->next_in[4]);
+                               strm->next_in += 6;
+                               strm->avail_in -= 6;
+                               br->cache_avail += 6 * 8;
+                               return (1);
+                       }
+                       break;
+               case 0:
+                       /* We have enough compressed data in
+                        * the cache buffer.*/ 
+                       return (1);
+               default:
+                       break;
+               }
                if (strm->avail_in == 0) {
                        /* There is not enough compressed data to fill up the
                         * cache buffer. */
                        return (0);
-               } else if (strm->avail_in >= 8) {
-                       int bytes;
-                       switch (bytes = (n >> 3)) {
-                       case 8:
-                               ds->br.cache_buffer =
-                                   archive_be64dec(strm->next_in);
-                               break;
-                       case 7:
-                               ds->br.cache_buffer =
-                                  (ds->br.cache_buffer << 56) |
-                                   archive_be64dec(strm->next_in) >> 8;
-                               break;
-                       case 6:
-                               ds->br.cache_buffer =
-                                  (ds->br.cache_buffer << 48) |
-                                  (((uint64_t)archive_be32dec(
-                                      strm->next_in)) << 16) |
-                                   archive_be16dec(&strm->next_in[4]);
-                               break;
-                       default:
-                               bytes = 0;
-                               break;
-                       }
-                       if (bytes) {
-                               strm->next_in += bytes;
-                               strm->avail_in -= bytes;
-                               strm->total_in += bytes;
-                               ds->br.cache_avail += bytes * 8;
-                               return (1);
-                       }
                }
-               ds->br.cache_buffer =
-                  (ds->br.cache_buffer << 8) | *strm->next_in++;
+               br->cache_buffer =
+                  (br->cache_buffer << 8) | *strm->next_in++;
                strm->avail_in--;
-               strm->total_in++;
-               ds->br.cache_avail += 8;
+               br->cache_avail += 8;
                n -= 8;
        }
-       return (1);
+#undef lzh_be16dec
+#undef lzh_be32dec
+#undef lzh_be64dec
 }
 
 /*
@@ -1911,13 +1989,8 @@ lzh_br_fillup(struct lzh_stream *strm)
  *    zeros are treated as the mark of the end of the data although the zeros
  *    is dummy, not the file data.
  */
-static int
-lzh_decode(struct lzh_stream *strm, int last)
-{
-       struct lzh_dec *ds = strm->ds;
-       struct lzh_br *br = &(ds->br);
-       int c = 0, i;
-       unsigned rbits;
+static int     lzh_read_blocks(struct lzh_stream *, int);
+static int     lzh_decode_blocks(struct lzh_stream *, int);
 #define ST_RD_BLOCK            0
 #define ST_RD_PT_1             1
 #define ST_RD_PT_2             2
@@ -1931,9 +2004,41 @@ lzh_decode(struct lzh_stream *strm, int last)
 #define ST_GET_POS_1           10
 #define ST_GET_POS_2           11
 #define ST_COPY_DATA           12
+
+static int
+lzh_decode(struct lzh_stream *strm, int last)
+{
+       struct lzh_dec *ds = strm->ds;
+       int64_t avail_in;
+       int r;
+
        if (ds->error)
                return (ds->error);
 
+       avail_in = strm->avail_in;
+       do {
+               if (ds->state < ST_GET_LITERAL)
+                       r = lzh_read_blocks(strm, last);
+               else {
+                       int64_t bytes_written = strm->avail_out;
+                       r = lzh_decode_blocks(strm, last);
+                       bytes_written -= strm->avail_out;
+                       strm->next_out += bytes_written;
+                       strm->total_out += bytes_written;
+               }
+       } while (r == 100);
+       strm->total_in += avail_in - strm->avail_in;
+       return (r);
+}
+
+static int
+lzh_read_blocks(struct lzh_stream *strm, int last)
+{
+       struct lzh_dec *ds = strm->ds;
+       struct lzh_br *br = &(ds->br);
+       int c = 0, i;
+       unsigned rbits;
+
        for (;;) {
                switch (ds->state) {
                case ST_RD_BLOCK:
@@ -2148,17 +2253,48 @@ lzh_decode(struct lzh_stream *strm, int last)
                        ds->reading_position = 1;
                        ds->state = ST_RD_PT_1;
                        break;
+               case ST_GET_LITERAL:
+                       return (100);
+               }
+       }
+failed:
+       return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzh_decode_blocks(struct lzh_stream *strm, int last)
+{
+       struct lzh_dec *ds = strm->ds;
+       struct lzh_br bre = ds->br;
+       struct huffman *lt = &(ds->lt);
+       struct huffman *pt = &(ds->pt);
+       unsigned char *outp = strm->next_out;
+       unsigned char *endp = outp + strm->avail_out;
+       unsigned char *w_buff = ds->w_buff;
+       unsigned char *lt_bitlen = lt->bitlen;
+       unsigned char *pt_bitlen = pt->bitlen;
+       int blocks_avail = ds->blocks_avail, c = 0;
+       int copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+       int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+       int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;
+       int state = ds->state;
+       uint16_t crc16 = ds->crc16;
+
+       for (;;) {
+               switch (state) {
                case ST_GET_LITERAL:
                        for (;;) {
-                               if (ds->blocks_avail == 0) {
+                               if (blocks_avail == 0) {
                                        /* We have decoded all blocks.
                                         * Let's handle next blocks. */
                                        ds->state = ST_RD_BLOCK;
-                                       break;
+                                       ds->br = bre;
+                                       ds->blocks_avail = 0;
+                                       ds->crc16 = crc16;
+                                       ds->w_pos = w_pos;
+                                       strm->avail_out = endp - outp;
+                                       return (100);
                                }
-                               if (strm->avail_out <= 0)
-                                       /* Output buffer is empty. */
-                                       return (ARCHIVE_OK);
 
                                /* lzh_br_read_ahead() always try to fill the
                                 * cache buffer up. In specific situation we
@@ -2167,145 +2303,168 @@ lzh_decode(struct lzh_stream *strm, int last)
                                 * determine if the cache buffer has some bits
                                 * as much as we need after lzh_br_read_ahead()
                                 * failed. */
-                               if (!lzh_br_read_ahead(strm, br,
-                                       ds->lt.max_bits) &&
-                                   !lzh_br_has(br, ds->lt.max_bits)) {
+                               if (!lzh_br_read_ahead(strm, &bre,
+                                   lt_max_bits) &&
+                                   !lzh_br_has(&bre, lt_max_bits)) {
                                        if (!last)
-                                               return (ARCHIVE_OK);
+                                               goto next_data;
                                        /* Remaining bits are less than
                                         * maximum bits(lt.max_bits) but maybe
                                         * it still remains as much as we need,
                                         * so we should try to use it with
                                         * dummy bits. */
-                                       rbits = lzh_br_bits_forced(
-                                           br, ds->lt.max_bits);
-                                       c = lzh_decode_huffman(&(ds->lt),rbits);
-                                       lzh_br_consume(br, ds->lt.bitlen[c]);
-                                       if (!lzh_br_has(br, 0))
+                                       c = lzh_decode_huffman(lt,
+                                             lzh_br_bits_forced(&bre,
+                                               lt_max_bits));
+                                       lzh_br_consume(&bre, lt_bitlen[c]);
+                                       if (!lzh_br_has(&bre, 0))
                                                goto failed;/* Over read. */
                                } else {
-                                       rbits = lzh_br_bits(br,
-                                           ds->lt.max_bits);
-                                       c = lzh_decode_huffman(&(ds->lt),rbits);
-                                       lzh_br_consume(br, ds->lt.bitlen[c]);
+                                       c = lzh_decode_huffman(lt,
+                                             lzh_br_bits(&bre, lt_max_bits));
+                                       lzh_br_consume(&bre, lt_bitlen[c]);
                                }
-                               ds->blocks_avail--;
+                               blocks_avail--;
                                if (c > UCHAR_MAX)
                                        /* Current block is a match data. */
                                        break;
+                               if (outp >= endp) {
+                                       /* Output buffer is empty. */
+                                       /* Back out the current block. */
+                                       blocks_avail++;
+                                       lzh_br_unconsume(&bre, lt_bitlen[c]);
+                                       goto next_data;
+                               }
                                /*
                                 * 'c' is exactly a literal code.
                                 */
                                /* Save a decoded code to reference it
                                 * afterward. */
-                               ds->w_buff[ds->w_pos] = c;
-                               ds->w_pos = (ds->w_pos + 1) & ds->w_mask;
-                               CRC16(ds->crc16, c);
+                               w_buff[w_pos] = c;
+                               w_pos = (w_pos + 1) & w_mask;
+                               CRC16(crc16, c);
                                /* Store the decoded code to the output
                                 * buffer. */
-                               *strm->next_out++ = c;
-                               strm->avail_out--;
-                               strm->total_out++;
+                               *outp++ = c;
                        }
-                       if (ds->state == ST_RD_BLOCK)
-                               break;
                        /* 'c' is the length of a match pattern we have
                         * already extracted, which has be stored in
                         * window(ds->w_buff). */
-                       ds->copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
+                       copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
                        /* FALL THROUGH */
                case ST_GET_POS_1:
                        /*
                         * Get a reference position. 
                         */
-                       if (!lzh_br_read_ahead(strm, br, ds->pt.max_bits) &&
-                           !lzh_br_has(br, ds->pt.max_bits)) {
+                       if (!lzh_br_read_ahead(strm, &bre, pt_max_bits) &&
+                           !lzh_br_has(&bre, pt_max_bits)) {
                                if (!last) {
-                                       ds->state = ST_GET_POS_1;
-                                       return (ARCHIVE_OK);
+                                       state = ST_GET_POS_1;
+                                       ds->copy_len = copy_len;
+                                       goto next_data;
                                }
-                               rbits = lzh_br_bits_forced(br, ds->pt.max_bits);
-                               c = lzh_decode_huffman(&(ds->pt), rbits);
-                               lzh_br_consume(br, ds->pt.bitlen[c]);
-                               if (!lzh_br_has(br, 0))
+                               copy_pos = lzh_decode_huffman(pt,
+                                   lzh_br_bits_forced(&bre, pt_max_bits));
+                               lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+                               if (!lzh_br_has(&bre, 0))
                                        goto failed;/* Over read. */
                        } else {
-                               rbits = lzh_br_bits(br, ds->pt.max_bits);
-                               c = lzh_decode_huffman(&(ds->pt), rbits);
-                               lzh_br_consume(br, ds->pt.bitlen[c]);
+                               copy_pos = lzh_decode_huffman(pt,
+                                   lzh_br_bits(&bre, pt_max_bits));
+                               lzh_br_consume(&bre, pt_bitlen[copy_pos]);
                        }
-                       ds->copy_pos = c;
                        /* FALL THROUGH */
                case ST_GET_POS_2:
-                       if (ds->copy_pos > 1) {
+                       if (copy_pos > 1) {
                                /* We need an additional adjustment number to
                                 * the position. */
-                               int p = ds->copy_pos - 1;
-                               if (!lzh_br_read_ahead(strm, br, p) &&
-                                   !lzh_br_has(br, p)) {
+                               int p = copy_pos - 1;
+                               if (!lzh_br_read_ahead(strm, &bre, p) &&
+                                   !lzh_br_has(&bre, p)) {
                                        if (last)
                                                goto failed;/* Truncated data.*/
-                                       ds->state = ST_GET_POS_2;
-                                       return (ARCHIVE_OK);
+                                       state = ST_GET_POS_2;
+                                       ds->copy_len = copy_len;
+                                       ds->copy_pos = copy_pos;
+                                       goto next_data;
                                }
-                               ds->copy_pos = (1 << p) + lzh_br_bits(br, p);
-                               lzh_br_consume(br, p);
+                               copy_pos = (1 << p) + lzh_br_bits(&bre, p);
+                               lzh_br_consume(&bre, p);
                        }
                        /* The position is actually a distance from the last
                         * code we had extracted and thus we have to convert
                         * it to a position of the window. */
-                       ds->copy_pos =
-                           (ds->w_pos - ds->copy_pos - 1) & ds->w_mask;
+                       copy_pos = (w_pos - copy_pos - 1) & w_mask;
                        /* FALL THROUGH */
                case ST_COPY_DATA:
                        /*
                         * Copy several bytes as extracted data from the window
                         * into the output buffer.
                         */
-                       do {
+                       for (;;) {
                                const unsigned char *s;
-                               unsigned char *d, *o;
-                               int l,ll;
-                               if (strm->avail_out <= 0) {
-                                       /* Output buffer is empty. */
-                                       ds->state = ST_COPY_DATA;
-                                       return (ARCHIVE_OK);
+                               int l;
+
+                               l = copy_len;
+                               if (copy_pos > w_pos) {
+                                       if (l > w_size - copy_pos)
+                                               l = w_size - copy_pos;
+                               } else {
+                                       if (l > w_size - w_pos)
+                                               l = w_size - w_pos;
                                }
-                               l = ds->copy_len;
-                               if (l > ds->w_size - ds->copy_pos)
-                                       l = ds->w_size - ds->copy_pos;
-                               if (l > ds->w_size - ds->w_pos)
-                                       l = ds->w_size - ds->w_pos;
-                               if (l > strm->avail_out)
-                                       l = (int)strm->avail_out;
-                               ll = l;
-                               s = &(ds->w_buff[ds->copy_pos]);
-                               d = &(ds->w_buff[ds->w_pos]);
-                               o = strm->next_out;
-                               if (l >= 8 && ((s + l < d) || (d + l < s))) {
-                                       memcpy(d, s, l);
-                                       memcpy(o, s, l);
-                                       ds->crc16 = lha_crc16(ds->crc16, s, l);
+                               if (outp + l >= endp)
+                                       l = endp - outp;
+                               s = w_buff + copy_pos;
+                               /*
+                                * We calculate CRC16 here becase of
+                                * increasing a performance of the
+                                * calculation so as to reduce L1 cache miss. 
+                                */
+                               if (l >= 8 && ((copy_pos + l < w_pos)
+                                   || (w_pos + l < copy_pos))) {
+                                       memcpy(w_buff + w_pos, s, l);
+                                       memcpy(outp, s, l);
+                                       crc16 = lha_crc16(crc16, s, l);
                                } else {
-                                       for (l = 0; l < ll; l++) {
-                                               c = o[l] = d[l] = s[l];
-                                               CRC16(ds->crc16, c);
+                                       unsigned char *d;
+                                       int li;
+
+                                       d = w_buff + w_pos;
+                                       for (li = 0; li < l; li++) {
+                                               c = outp[li] = d[li] = s[li];
+                                               CRC16(crc16, c);
                                        }
                                }
-                               strm->next_out += ll;
-                               strm->avail_out -= ll;
-                               strm->total_out += ll;
-                               ds->copy_pos =
-                                   (ds->copy_pos + ll) & ds->w_mask;
-                               ds->w_pos = (ds->w_pos + ll) & ds->w_mask;
-                               ds->copy_len -= ll;
-                       } while (ds->copy_len > 0);
-                       ds->state = ST_GET_LITERAL;
+                               outp += l;
+                               copy_pos = (copy_pos + l) & w_mask;
+                               w_pos = (w_pos + l) & w_mask;
+                               if (copy_len <= l)
+                                       /* A copy of current pattern ended. */
+                                       break;
+                               copy_len -= l;
+                               if (outp >= endp) {
+                                       /* Output buffer is empty. */
+                                       state = ST_COPY_DATA;
+                                       ds->copy_len = copy_len;
+                                       ds->copy_pos = copy_pos;
+                                       goto next_data;
+                               }
+                       }
+                       state = ST_GET_LITERAL;
                        break;
                }
        }
 failed:
        return (ds->error = ARCHIVE_FAILED);
+next_data:
+       ds->br = bre;
+       ds->blocks_avail = blocks_avail;
+       ds->crc16 = crc16;
+       ds->state = state;
+       ds->w_pos = w_pos;
+       strm->avail_out = endp - outp;
+       return (ARCHIVE_OK);
 }
 
 static int