From: Michihiro NAKAJIMA Date: Wed, 3 Aug 2011 03:44:34 +0000 (-0400) Subject: Rework lzh and lzx decoding to improve its performance. X-Git-Tag: v3.0.0a~216 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c247a74f1dc6e6efdb3f47243bc72ac4e1016e09;p=thirdparty%2Flibarchive.git Rework lzh and lzx decoding to improve its performance. SVN-Revision: 3515 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c68b8d9e9..10dc5ffea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index ff564c976..fb0d0ecb6 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -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 header file. */ +#cmakedefine HAVE_ENDIAN_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H 1 @@ -808,6 +811,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_EA_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_ENDIAN_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_EXTATTR_H 1 diff --git a/configure.ac b/configure.ac index 33268b7fa..ff60764d2 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index 87cb47b3e..6ed59a249 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -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 diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index 79826efca..6bbdf93d9 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -25,9 +25,15 @@ #include "archive_platform.h" +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif #ifdef HAVE_SYS_TYPES_H #include #endif +#ifdef HAVE_ENDIAN_H +#include +#endif #ifdef HAVE_ERRNO_H #include #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