#include #include #include "halibut.h" #include "huffman.h" #include "lz77.h" #include "lzx.h" #define OUR_LZX_WINSIZE 0x10000 #define LZX_MINMATCHLEN 2 #define LZX_MAXMATCHLEN 257 int lzx_compute_position_slot(int pos, int *footer_bits) { if (pos < 4) { /* The bottom four position slots cover one value each. */ *footer_bits = 0; return pos; } else if (pos >= 0x40000) { /* _All_ slots from 36 onwards are 2^17 values wide. */ *footer_bits = 17; return 34 + (pos >> 17); } else { /* In between, there are two slots for each power-of-2 size, * so that slots 4,5 have width 2^1, 6,7 have width 2^2, 8,9 * have width 2^3, ..., and 34,35 have width 2^16. */ int bits = 16; int shifted = pos; if (shifted < (1<<(18-8))) shifted <<= 8, bits -= 8; if (shifted < (1<<(18-4))) shifted <<= 4, bits -= 4; if (shifted < (1<<(18-2))) shifted <<= 2, bits -= 2; if (shifted < (1<<(18-1))) shifted <<= 1, bits -= 1; *footer_bits = bits; return 2 + 2*bits + ((shifted >> 16) & 1); } } typedef enum LZXSymType { LST_MAINTREE, LST_LENTREE, LST_ALIGNOFFTREE, LST_MAINTREE_PRETREE_1, LST_MAINTREE_PRETREE_2, LST_LENTREE_PRETREE, LST_NTREES, dummy_enum_const = LST_NTREES-1, LST_REALIGN_BITSTREAM, LST_RAWBITS_BASE /* add the number of actual bits to this code */ } LZXSymType; typedef struct LZXSym { LZXSymType type; int value; } LZXSym; typedef struct LZXBuffer { LZXSym *syms; int nsyms, symsize; } LZXBuffer; typedef struct LZXInfo { LZXBuffer *buf; int r0, r1, r2; /* saved match offsets */ } LZXInfo; static void lzx_buffer_init(LZXBuffer *buf) { buf->syms = NULL; buf->nsyms = buf->symsize = 0; } static void lzx_addsym(LZXBuffer *buf, LZXSymType type, int value) { if (buf->nsyms >= buf->symsize) { assert(buf->nsyms == buf->symsize); buf->symsize = buf->nsyms * 5 / 4 + 16384; buf->syms = sresize(buf->syms, buf->symsize, LZXSym); } buf->syms[buf->nsyms].type = type; buf->syms[buf->nsyms].value = value; buf->nsyms++; } static void lzx_literal(struct LZ77Context *ctx, unsigned char c) { LZXBuffer *buf = ((LZXInfo *)ctx->userdata)->buf; lzx_addsym(buf, LST_MAINTREE, c); } static void lzx_match(struct LZ77Context *ctx, int match_offset, int totallen) { LZXInfo *info = (LZXInfo *)ctx->userdata; LZXBuffer *buf = info->buf; /* * First, this variant of LZX has a maximum match length of 257 * bytes, so if lz77.c reports a longer match than that, we must * break it up. */ while (totallen > 0) { int len, length_header, length_footer, len_pos_header; int formatted_offset, position_slot, position_verbatim_bits; int position_verbatim_value, position_aligned_offset; if (totallen <= LZX_MAXMATCHLEN) { /* We can emit all of the (remaining) match length in one go. */ len = totallen; } else if (totallen >= LZX_MAXMATCHLEN+LZX_MINMATCHLEN) { /* There's enough match left that we can emit a * maximum-length chunk and still be assured of being able * to emit what's left as a viable followup match. */ len = LZX_MAXMATCHLEN; } else { /* The in-between case, where we have _only just_ too long * a match to emit in one go, so that if we emitted a * max-size chunk then what's left would be under the min * size and we couldn't emit it. */ len = totallen - LZX_MINMATCHLEN; } totallen -= len; /* * Now we're outputting a single LZX-level match of length * 'len'. Break the length up into a 'header' (included in the * starting LST_MAINTREE symbol) and a 'footer' (tacked on * afterwards using LST_LENTREE). */ if (len < 9) { length_header = len - 2; /* in the range {0,...,6} */ length_footer = -1; /* not transmitted at all */ } else { length_header = 7; /* header indicates more to come */ length_footer = len - 9; /* in the range {0,...,248} */ } /* * Meanwhile, the raw backward distance is first transformed * into the 'formatted offset', by either adding 2 or using * one of the low-numbered special codes meaning to use one of * the three most recent match distances. */ if (match_offset == info->r0) { /* Reuse the most recent distance */ formatted_offset = 0; } else if (match_offset == info->r1) { /* Reuse the 2nd most recent, and swap it into first place */ int tmp = info->r1; info->r1 = info->r0; info->r0 = tmp; formatted_offset = 1; } else if (match_offset == info->r2) { /* Reuse the 3rd most recent and swap it to first place. * This is intentionally not quite a move-to-front * shuffle, which would permute (r0,r1,r2)->(r2,r0,r1); MS * decided that just swapping r0 with r2 was a better * performance tradeoff. */ int tmp = info->r2; info->r2 = info->r0; info->r0 = tmp; formatted_offset = 2; } else { /* This offset matches none of the three saved values. * Put it in r0, and move up the rest of the list. */ info->r2 = info->r1; info->r1 = info->r0; info->r0 = match_offset; formatted_offset = match_offset + 2; } /* * The formatted offset now breaks up into a 'position slot' * (encoded as part of the starting symbol) and an offset from * the smallest position value covered by that slot. The * system of slots is designed so that every slot's width is a * power of two and its base value is a multiple of its width, * so we can get the offset just by taking the bottom n bits * of the full formatted offset, once the choice of position * slot tells us what n is. */ position_slot = lzx_compute_position_slot( formatted_offset, &position_verbatim_bits); position_verbatim_value = formatted_offset & ((1 << position_verbatim_bits)-1); /* * If there are three or more additional bits, then the last 3 * of them are (potentially, depending on block type which we * haven't decided about yet) transmitted using the aligned * offset tree. The rest are sent verbatim. */ if (position_verbatim_bits >= 3) { position_aligned_offset = position_verbatim_value & 7; position_verbatim_bits -= 3; position_verbatim_value >>= 3; } else { position_aligned_offset = -1; /* not transmitted */ } /* * Combine the length header and position slot into the full * set of information encoded by the starting symbol. */ len_pos_header = position_slot * 8 + length_header; /* * And now we've finished figuring out _what_ to output, so * output it. */ lzx_addsym(buf, LST_MAINTREE, 256 + len_pos_header); if (length_footer >= 0) lzx_addsym(buf, LST_LENTREE, length_footer); if (position_verbatim_bits > 0) lzx_addsym(buf, LST_RAWBITS_BASE + position_verbatim_bits, position_verbatim_value); if (position_aligned_offset >= 0) lzx_addsym(buf, LST_ALIGNOFFTREE, position_aligned_offset); } } void lzx_lz77_inner(LZXInfo *info, const unsigned char *data, int len) { struct LZ77Context lz77c; lz77_init(&lz77c, OUR_LZX_WINSIZE); lz77c.literal = lzx_literal; lz77c.match = lzx_match; lz77c.userdata = info; lz77_compress(&lz77c, data, len, TRUE); lz77_cleanup(&lz77c); } void lzx_lz77(LZXBuffer *buf, const unsigned char *data, int totallen, int realign_interval) { LZXInfo info; info.r0 = info.r1 = info.r2 = 1; info.buf = buf; while (totallen > 0) { int thislen = totallen < realign_interval ? totallen : realign_interval; lzx_lz77_inner(&info, data, thislen); data += thislen; totallen -= thislen; if (totallen > 0) lzx_addsym(info.buf, LST_REALIGN_BITSTREAM, 0); } } typedef struct LZXHuf { int nsyms; unsigned char *lengths; unsigned char *oldlengths; /* for pretree encoding to diff against */ int *codes; } LZXHuf; typedef struct LZXHufs { LZXHuf hufs[LST_NTREES]; } LZXHufs; void lzx_build_tree(LZXSym *syms, int nsyms, LZXSymType which, LZXHufs *hufs) { int i, max_code_len; int *freqs; LZXHuf *huf = &hufs->hufs[which]; switch (which) { default: assert(0 && "Bad lzx_build_tree tree type"); case LST_MAINTREE: /* * Trees encoded via a pretree have a max code length of 16, * because that's the limit of what the pretree alphabet can * represent. */ max_code_len = 16; /* * Number of symbols in the main tree is 256 literals, plus 8n * match header symbols where n is the largest position slot * number that might be needed to address any offset in the * window. */ { int ignored, last_slot; last_slot = lzx_compute_position_slot(OUR_LZX_WINSIZE-1, &ignored); huf->nsyms = 8 * (last_slot+1) + 256; } break; case LST_LENTREE: max_code_len = 16; /* pretree again */ huf->nsyms = 249; /* a fixed value in the spec */ break; case LST_MAINTREE_PRETREE_1: case LST_MAINTREE_PRETREE_2: case LST_LENTREE_PRETREE: /* Pretree code lengths are stored in 4-bit fields, so they * can't go above 15. There are a standard 20 symbols in the * pretree alphabet. */ max_code_len = 15; huf->nsyms = 20; break; case LST_ALIGNOFFTREE: /* The aligned-offset tree has 8 elements stored in 3-bit * fields. */ max_code_len = 7; huf->nsyms = 8; break; } freqs = snewn(huf->nsyms, int); /* * Count up the symbol frequencies. */ for (i = 0; i < huf->nsyms; i++) freqs[i] = 0; for (i = 0; i < nsyms; i++) if (syms[i].type == which) freqs[syms[i].value]++; /* * Build the Huffman table. */ huf->lengths = snewn(huf->nsyms, unsigned char); build_huffman_tree(freqs, huf->lengths, huf->nsyms, max_code_len); huf->codes = snewn(huf->nsyms, int); compute_huffman_codes(huf->lengths, huf->codes, huf->nsyms); /* * Cleanup. */ sfree(freqs); } void lzx_tree_with_pretree(LZXHuf *huf, int symoffset, int symlimit, LZXBuffer *buf, LZXSymType pretree_symtype) { int i, r; if (!huf->oldlengths) { huf->oldlengths = snewn(huf->nsyms, unsigned char); for (i = 0; i < huf->nsyms; i++) huf->oldlengths[i] = 0; } for (i = symoffset; i < symlimit; i++) { for (r = 1; i+r < symlimit; r++) if (huf->lengths[i+r] != huf->lengths[i]) break; if (r >= 4) { /* * We have at least one run of the same code length long * enough to use one of the run-length encoding symbols. */ while (r >= 4) { int thisrun; if (huf->lengths[i] == 0) { thisrun = r > 20+31 ? 20+31 : r; if (thisrun >= 20) { lzx_addsym(buf, pretree_symtype, 18); lzx_addsym(buf, LST_RAWBITS_BASE + 5, thisrun - 20); } else { lzx_addsym(buf, pretree_symtype, 17); lzx_addsym(buf, LST_RAWBITS_BASE + 4, thisrun - 4); } } else { thisrun = r > 5 ? 5 : r; lzx_addsym(buf, pretree_symtype, 19); lzx_addsym(buf, LST_RAWBITS_BASE + 1, thisrun - 4); lzx_addsym(buf, pretree_symtype, (huf->oldlengths[i]-huf->lengths[i] + 17) % 17); } r -= thisrun; i += thisrun; } if (r == 0) { i--; /* compensate for normal loop increment */ continue; } } /* * Otherwise, emit a normal non-encoded symbol. */ lzx_addsym(buf, pretree_symtype, (huf->oldlengths[i]-huf->lengths[i] + 17) % 17); } } void lzx_tree_simple(LZXHuf *huf, LZXBuffer *buf, int bits) { int i; for (i = 0; i < huf->nsyms; i++) lzx_addsym(buf, LST_RAWBITS_BASE + bits, huf->lengths[i]); } typedef struct LZXBitstream { struct LZXEncodedFile *ef; size_t data_size, resets_size; unsigned short bitbuffer; int nbits; int first_block; } LZXBitstream; void lzx_write_bits(LZXBitstream *bs, int value, int bits) { while (bs->nbits + bits >= 16) { int thisbits = 16 - bs->nbits; bs->bitbuffer = (bs->bitbuffer << thisbits) | (value >> (bits-thisbits)); if (bs->ef->data_len+2 > bs->data_size) { bs->data_size = bs->ef->data_len * 5 / 4 + 65536; bs->ef->data = sresize(bs->ef->data, bs->data_size, unsigned char); } bs->ef->data[bs->ef->data_len++] = bs->bitbuffer; bs->ef->data[bs->ef->data_len++] = bs->bitbuffer >> 8; bs->bitbuffer = 0; bs->nbits = 0; bits -= thisbits; value &= (1<bitbuffer = (bs->bitbuffer << bits) | value; bs->nbits += bits; } void lzx_realign(LZXBitstream *bs) { lzx_write_bits(bs, 0, 15 & -(unsigned)bs->nbits); } void lzx_write_reset_table_entry(LZXBitstream *bs) { lzx_write_bits(bs, 0, 15 & -(unsigned)bs->nbits); if (bs->ef->n_resets >= bs->resets_size) { bs->resets_size = bs->ef->n_resets * 5 / 4 + 256; bs->ef->reset_byte_offsets = sresize(bs->ef->reset_byte_offsets, bs->resets_size, size_t); } bs->ef->reset_byte_offsets[bs->ef->n_resets++] = bs->ef->data_len; } void lzx_huf_encode(LZXSym *syms, int nsyms, LZXHufs *hufs, LZXBitstream *bs) { int i; for (i = 0; i < nsyms; i++) { LZXSymType type = syms[i].type; int value = syms[i].value; if (type >= LST_RAWBITS_BASE) { lzx_write_bits(bs, value, type - LST_RAWBITS_BASE); } else if (type == LST_REALIGN_BITSTREAM) { /* Realign the bitstream to a 16-bit boundary, and write a * reset table entry giving the resulting byte offset. */ lzx_realign(bs); lzx_write_reset_table_entry(bs); } else { lzx_write_bits(bs, hufs->hufs[type].codes[value], hufs->hufs[type].lengths[value]); } } } void lzx_encode_block(LZXSym *syms, int nsyms, int blocksize, LZXHufs *hufs, LZXBitstream *bs) { LZXBuffer header[8]; int i, blocktype; for (i = 0; i < (int)lenof(header); i++) lzx_buffer_init(&header[i]); /* * Build the Huffman trees for the main alphabets used in the * block. */ lzx_build_tree(syms, nsyms, LST_MAINTREE, hufs); lzx_build_tree(syms, nsyms, LST_LENTREE, hufs); lzx_build_tree(syms, nsyms, LST_ALIGNOFFTREE, hufs); /* * Encode each of those as a sequence of pretree symbols. */ lzx_tree_with_pretree(&hufs->hufs[LST_MAINTREE], 0, 256, &header[3], LST_MAINTREE_PRETREE_1); lzx_tree_with_pretree(&hufs->hufs[LST_MAINTREE], 256, hufs->hufs[LST_MAINTREE].nsyms, &header[5], LST_MAINTREE_PRETREE_2); lzx_tree_with_pretree(&hufs->hufs[LST_LENTREE], 0, hufs->hufs[LST_LENTREE].nsyms, &header[7], LST_LENTREE_PRETREE); /* * Build the pretree for each of those encodings. */ lzx_build_tree(header[3].syms, header[3].nsyms, LST_MAINTREE_PRETREE_1, hufs); lzx_build_tree(header[5].syms, header[5].nsyms, LST_MAINTREE_PRETREE_2, hufs); lzx_build_tree(header[7].syms, header[7].nsyms, LST_LENTREE_PRETREE, hufs); /* * Decide whether we're keeping the aligned offset tree or not. */ { int with, without; with = 3*8; /* cost of transmitting tree */ without = 0; /* or not */ for (i = 0; i < nsyms; i++) if (syms[i].type == LST_ALIGNOFFTREE) { with += hufs->hufs[LST_ALIGNOFFTREE].lengths[syms[i].value]; without += 3; } if (with < without) { /* Yes, it's a win to use the aligned offset tree. */ blocktype = 2; } else { /* No, we do better by throwing it away. */ blocktype = 1; /* Easiest way to simulate that is to pretend we're still * using an aligned offset tree in the encoding, but to * chuck away our code lengths and replace them with the * fixed-length trivial tree. */ for (i = 0; i < 8; i++) { hufs->hufs[LST_ALIGNOFFTREE].lengths[i] = 3; hufs->hufs[LST_ALIGNOFFTREE].codes[i] = i; } } } /* * Encode all the simply encoded trees (the three pretrees and the * aligned offset tree). */ lzx_tree_simple(&hufs->hufs[LST_MAINTREE_PRETREE_1], &header[2], 4); lzx_tree_simple(&hufs->hufs[LST_MAINTREE_PRETREE_2], &header[4], 4); lzx_tree_simple(&hufs->hufs[LST_LENTREE_PRETREE], &header[6], 4); if (blocktype == 2) lzx_tree_simple(&hufs->hufs[LST_ALIGNOFFTREE], &header[1], 3); /* * Top-level block header. */ if (bs->first_block) { /* * Also include the whole-file header which says whether E8 * call translation is on. We never turn it on, because we * don't support it (since in this use case it doesn't seem * likely to be particularly useful anyway). * * It looks like a layer violation to put the output of this * whole-file header inside the per-block function like this, * but in fact it has to be done here because the first reset * table entry really is supposed to point to the _start_ of * the whole-file header. */ lzx_addsym(&header[0], LST_RAWBITS_BASE + 1, 0); bs->first_block = FALSE; } lzx_addsym(&header[0], LST_RAWBITS_BASE + 3, blocktype); lzx_addsym(&header[0], LST_RAWBITS_BASE + 24, blocksize); /* * Ensure the bit stream starts off aligned, and output an initial * reset-table entry. */ lzx_realign(bs); lzx_write_reset_table_entry(bs); /* * Write out all of our symbol sequences in order: all of those * assorted header fragments, then the main LZ77 token sequence. */ for (i = 0; i < (int)lenof(header); i++) lzx_huf_encode(header[i].syms, header[i].nsyms, hufs, bs); lzx_huf_encode(syms, nsyms, hufs, bs); /* * Clean up. */ for (i = 0; i < (int)lenof(header); i++) sfree(header[i].syms); for (i = 0; i < (int)lenof(hufs->hufs); i++) { sfree(hufs->hufs[i].codes); sfree(hufs->hufs[i].lengths); } } struct LZXEncodedFile *lzx(const void *vdata, int totallen, int realign_interval, int reset_interval) { const unsigned char *data = (const unsigned char *)vdata; LZXBitstream bs; LZXHufs hufs; int i; bs.ef = snew(struct LZXEncodedFile); bs.ef->data = NULL; bs.ef->reset_byte_offsets = NULL; bs.ef->data_len = bs.data_size = 0; bs.ef->n_resets = bs.resets_size = 0; bs.bitbuffer = 0; bs.nbits = 0; for (i = 0; i < (int)lenof(hufs.hufs); i++) hufs.hufs[i].oldlengths = NULL; while (totallen > 0) { int thislen = totallen < reset_interval ? totallen : reset_interval; LZXBuffer buf; lzx_buffer_init(&buf); lzx_lz77(&buf, data, thislen, realign_interval); data += thislen; totallen -= thislen; /* * Block boundaries are chosen completely trivially: since we * have to terminate a block every time we reach the (fairly * short) reset interval in any case, it doesn't hurt us much * to just fix the assumption that every (reset_interval) * bytes of the input turn into exactly one block, i.e. the * whole of buf.syms that we just constructed is output in one * go. We _could_ try improving on this by clever * block-boundary heuristics, but I don't really think it's * worth it. */ bs.first_block = TRUE; /* reset every time we reset the LZ state */ lzx_encode_block(buf.syms, buf.nsyms, thislen, &hufs, &bs); sfree(buf.syms); } for (i = 0; i < (int)lenof(hufs.hufs); i++) sfree(hufs.hufs[i].oldlengths); /* Realign to a 16-bit boundary, i.e. flush out any last few * unwritten bits. */ lzx_realign(&bs); return bs.ef; } #ifdef LZX_TEST /* gcc -g -O0 -DLZX_TEST -o lzxtest -Icharset lzx.c lz77.c huffman.c malloc.c */ #include int main(int argc, char **argv) { FILE *fp; long insize; unsigned char *inbuf; struct LZXEncodedFile *ef; if (argc != 3) errx(1, "expected infile and outfile arguments"); fp = fopen(argv[1], "rb"); if (!fp) err(1, "%s: open", argv[1]); fseek(fp, 0, SEEK_END); insize = ftell(fp); rewind(fp); inbuf = snewn(insize, unsigned char); fread(inbuf, 1, insize, fp); fclose(fp); ef = lzx(inbuf, insize, 0x8000, 0x10000); fp = fopen(argv[2], "wb"); if (!fp) err(1, "%s: open", argv[2]); fwrite(ef->data, 1, ef->data_len, fp); fclose(fp); sfree(ef->data); sfree(ef->reset_byte_offsets); sfree(ef); sfree(inbuf); return 0; } wchar_t *ustrdup(wchar_t const *s) { assert(0 && "should be unused"); } void fatalerr_nomemory(void) { errx(1, "out of memory"); } #endif