📄 sshzlib.c
字号:
/* * Zlib (RFC1950 / RFC1951) compression for PuTTY. * * There will no doubt be criticism of my decision to reimplement * Zlib compression from scratch instead of using the existing zlib * code. People will cry `reinventing the wheel'; they'll claim * that the `fundamental basis of OSS' is code reuse; they'll want * to see a really good reason for me having chosen not to use the * existing code. * * Well, here are my reasons. Firstly, I don't want to link the * whole of zlib into the PuTTY binary; PuTTY is justifiably proud * of its small size and I think zlib contains a lot of unnecessary * baggage for the kind of compression that SSH requires. * * Secondly, I also don't like the alternative of using zlib.dll. * Another thing PuTTY is justifiably proud of is its ease of * installation, and the last thing I want to do is to start * mandating DLLs. Not only that, but there are two _kinds_ of * zlib.dll kicking around, one with C calling conventions on the * exported functions and another with WINAPI conventions, and * there would be a significant danger of getting the wrong one. * * Thirdly, there seems to be a difference of opinion on the IETF * secsh mailing list about the correct way to round off a * compressed packet and start the next. In particular, there's * some talk of switching to a mechanism zlib isn't currently * capable of supporting (see below for an explanation). Given that * sort of uncertainty, I thought it might be better to have code * that will support even the zlib-incompatible worst case. * * Fourthly, it's a _second implementation_. Second implementations * are fundamentally a Good Thing in standardisation efforts. The * difference of opinion mentioned above has arisen _precisely_ * because there has been only one zlib implementation and * everybody has used it. I don't intend that this should happen * again. */#include <stdlib.h>#include <assert.h>#ifdef ZLIB_STANDALONE/* * This module also makes a handy zlib decoding tool for when * you're picking apart Zip files or PDFs or PNGs. If you compile * it with ZLIB_STANDALONE defined, it builds on its own and * becomes a command-line utility. * * Therefore, here I provide a self-contained implementation of the * macros required from the rest of the PuTTY sources. */#define snew(type) ( (type *) malloc(sizeof(type)) )#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) )#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )#define sfree(x) ( free((x)) )#else#include "ssh.h"#endif#ifndef FALSE#define FALSE 0#define TRUE (!FALSE)#endif/* ---------------------------------------------------------------------- * Basic LZ77 code. This bit is designed modularly, so it could be * ripped out and used in a different LZ77 compressor. Go to it, * and good luck :-) */struct LZ77InternalContext;struct LZ77Context { struct LZ77InternalContext *ictx; void *userdata; void (*literal) (struct LZ77Context * ctx, unsigned char c); void (*match) (struct LZ77Context * ctx, int distance, int len);};/* * Initialise the private fields of an LZ77Context. It's up to the * user to initialise the public fields. */static int lz77_init(struct LZ77Context *ctx);/* * Supply data to be compressed. Will update the private fields of * the LZ77Context, and will call literal() and match() to output. * If `compress' is FALSE, it will never emit a match, but will * instead call literal() for everything. */static void lz77_compress(struct LZ77Context *ctx, unsigned char *data, int len, int compress);/* * Modifiable parameters. */#define WINSIZE 32768 /* window size. Must be power of 2! */#define HASHMAX 2039 /* one more than max hash value */#define MAXMATCH 32 /* how many matches we track */#define HASHCHARS 3 /* how many chars make a hash *//* * This compressor takes a less slapdash approach than the * gzip/zlib one. Rather than allowing our hash chains to fall into * disuse near the far end, we keep them doubly linked so we can * _find_ the far end, and then every time we add a new byte to the * window (thus rolling round by one and removing the previous * byte), we can carefully remove the hash chain entry. */#define INVALID -1 /* invalid hash _and_ invalid offset */struct WindowEntry { short next, prev; /* array indices within the window */ short hashval;};struct HashEntry { short first; /* window index of first in chain */};struct Match { int distance, len;};struct LZ77InternalContext { struct WindowEntry win[WINSIZE]; unsigned char data[WINSIZE]; int winpos; struct HashEntry hashtab[HASHMAX]; unsigned char pending[HASHCHARS]; int npending;};static int lz77_hash(unsigned char *data){ return (257 * data[0] + 263 * data[1] + 269 * data[2]) % HASHMAX;}static int lz77_init(struct LZ77Context *ctx){ struct LZ77InternalContext *st; int i; st = snew(struct LZ77InternalContext); if (!st) return 0; ctx->ictx = st; for (i = 0; i < WINSIZE; i++) st->win[i].next = st->win[i].prev = st->win[i].hashval = INVALID; for (i = 0; i < HASHMAX; i++) st->hashtab[i].first = INVALID; st->winpos = 0; st->npending = 0; return 1;}static void lz77_advance(struct LZ77InternalContext *st, unsigned char c, int hash){ int off; /* * Remove the hash entry at winpos from the tail of its chain, * or empty the chain if it's the only thing on the chain. */ if (st->win[st->winpos].prev != INVALID) { st->win[st->win[st->winpos].prev].next = INVALID; } else if (st->win[st->winpos].hashval != INVALID) { st->hashtab[st->win[st->winpos].hashval].first = INVALID; } /* * Create a new entry at winpos and add it to the head of its * hash chain. */ st->win[st->winpos].hashval = hash; st->win[st->winpos].prev = INVALID; off = st->win[st->winpos].next = st->hashtab[hash].first; st->hashtab[hash].first = st->winpos; if (off != INVALID) st->win[off].prev = st->winpos; st->data[st->winpos] = c; /* * Advance the window pointer. */ st->winpos = (st->winpos + 1) & (WINSIZE - 1);}#define CHARAT(k) ( (k)<0 ? st->data[(st->winpos+k)&(WINSIZE-1)] : data[k] )static void lz77_compress(struct LZ77Context *ctx, unsigned char *data, int len, int compress){ struct LZ77InternalContext *st = ctx->ictx; int i, hash, distance, off, nmatch, matchlen, advance; struct Match defermatch, matches[MAXMATCH]; int deferchr; /* * Add any pending characters from last time to the window. (We * might not be able to.) */ for (i = 0; i < st->npending; i++) { unsigned char foo[HASHCHARS]; int j; if (len + st->npending - i < HASHCHARS) { /* Update the pending array. */ for (j = i; j < st->npending; j++) st->pending[j - i] = st->pending[j]; break; } for (j = 0; j < HASHCHARS; j++) foo[j] = (i + j < st->npending ? st->pending[i + j] : data[i + j - st->npending]); lz77_advance(st, foo[0], lz77_hash(foo)); } st->npending -= i; defermatch.len = 0; deferchr = '\0'; while (len > 0) { /* Don't even look for a match, if we're not compressing. */ if (compress && len >= HASHCHARS) { /* * Hash the next few characters. */ hash = lz77_hash(data); /* * Look the hash up in the corresponding hash chain and see * what we can find. */ nmatch = 0; for (off = st->hashtab[hash].first; off != INVALID; off = st->win[off].next) { /* distance = 1 if off == st->winpos-1 */ /* distance = WINSIZE if off == st->winpos */ distance = WINSIZE - (off + WINSIZE - st->winpos) % WINSIZE; for (i = 0; i < HASHCHARS; i++) if (CHARAT(i) != CHARAT(i - distance)) break; if (i == HASHCHARS) { matches[nmatch].distance = distance; matches[nmatch].len = 3; if (++nmatch >= MAXMATCH) break; } } } else { nmatch = 0; hash = INVALID; } if (nmatch > 0) { /* * We've now filled up matches[] with nmatch potential * matches. Follow them down to find the longest. (We * assume here that it's always worth favouring a * longer match over a shorter one.) */ matchlen = HASHCHARS; while (matchlen < len) { int j; for (i = j = 0; i < nmatch; i++) { if (CHARAT(matchlen) == CHARAT(matchlen - matches[i].distance)) { matches[j++] = matches[i]; } } if (j == 0) break; matchlen++; nmatch = j; } /* * We've now got all the longest matches. We favour the * shorter distances, which means we go with matches[0]. * So see if we want to defer it or throw it away. */ matches[0].len = matchlen; if (defermatch.len > 0) { if (matches[0].len > defermatch.len + 1) { /* We have a better match. Emit the deferred char, * and defer this match. */ ctx->literal(ctx, (unsigned char) deferchr); defermatch = matches[0]; deferchr = data[0]; advance = 1; } else { /* We don't have a better match. Do the deferred one. */ ctx->match(ctx, defermatch.distance, defermatch.len); advance = defermatch.len - 1; defermatch.len = 0; } } else { /* There was no deferred match. Defer this one. */ defermatch = matches[0]; deferchr = data[0]; advance = 1; } } else { /* * We found no matches. Emit the deferred match, if * any; otherwise emit a literal. */ if (defermatch.len > 0) { ctx->match(ctx, defermatch.distance, defermatch.len); advance = defermatch.len - 1; defermatch.len = 0; } else { ctx->literal(ctx, data[0]); advance = 1; } } /* * Now advance the position by `advance' characters, * keeping the window and hash chains consistent. */ while (advance > 0) { if (len >= HASHCHARS) { lz77_advance(st, *data, lz77_hash(data)); } else { st->pending[st->npending++] = *data; } data++; len--; advance--; } }}/* ---------------------------------------------------------------------- * Zlib compression. We always use the static Huffman tree option. * Mostly this is because it's hard to scan a block in advance to * work out better trees; dynamic trees are great when you're * compressing a large file under no significant time constraint, * but when you're compressing little bits in real time, things get * hairier. * * I suppose it's possible that I could compute Huffman trees based * on the frequencies in the _previous_ block, as a sort of * heuristic, but I'm not confident that the gain would balance out * having to transmit the trees. */struct Outbuf { unsigned char *outbuf; int outlen, outsize; unsigned long outbits; int noutbits; int firstblock; int comp_disabled;};static void outbits(struct Outbuf *out, unsigned long bits, int nbits){ assert(out->noutbits + nbits <= 32); out->outbits |= bits << out->noutbits; out->noutbits += nbits; while (out->noutbits >= 8) { if (out->outlen >= out->outsize) { out->outsize = out->outlen + 64; out->outbuf = sresize(out->outbuf, out->outsize, unsigned char); } out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF); out->outbits >>= 8; out->noutbits -= 8; }}static const unsigned char mirrorbytes[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,};typedef struct { short code, extrabits; int min, max;} coderecord;static const coderecord lencodes[] = { {257, 0, 3, 3}, {258, 0, 4, 4}, {259, 0, 5, 5}, {260, 0, 6, 6}, {261, 0, 7, 7}, {262, 0, 8, 8}, {263, 0, 9, 9}, {264, 0, 10, 10}, {265, 1, 11, 12}, {266, 1, 13, 14}, {267, 1, 15, 16}, {268, 1, 17, 18}, {269, 2, 19, 22}, {270, 2, 23, 26}, {271, 2, 27, 30}, {272, 2, 31, 34}, {273, 3, 35, 42}, {274, 3, 43, 50}, {275, 3, 51, 58}, {276, 3, 59, 66}, {277, 4, 67, 82}, {278, 4, 83, 98}, {279, 4, 99, 114}, {280, 4, 115, 130}, {281, 5, 131, 162}, {282, 5, 163, 194}, {283, 5, 195, 226}, {284, 5, 227, 257}, {285, 0, 258, 258},};static const coderecord distcodes[] = { {0, 0, 1, 1}, {1, 0, 2, 2}, {2, 0, 3, 3}, {3, 0, 4, 4}, {4, 1, 5, 6}, {5, 1, 7, 8},
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -