flate

deflate implementation
git clone git://git.suckless.org/flate
Log | Files | Refs | README

sflate.c (9978B)


      1 #include <stdlib.h>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include "flate.h"
      5 
      6 static void set32(uchar *p, uint n) {
      7 	p[0] = n >> 24;
      8 	p[1] = n >> 16;
      9 	p[2] = n >> 8;
     10 	p[3] = n;
     11 }
     12 
     13 static void set32le(uchar *p, uint n) {
     14 	p[0] = n;
     15 	p[1] = n >> 8;
     16 	p[2] = n >> 16;
     17 	p[3] = n >> 24;
     18 }
     19 
     20 static int check32(uchar *p, uint n) {
     21 	return n == ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
     22 }
     23 
     24 static int check32le(uchar *p, uint n) {
     25 	return n == (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
     26 }
     27 
     28 enum {
     29 	ZlibCM    = 7 << 4,
     30 	ZlibCINFO = 8,
     31 	ZlibFLEV  = 3 << 6,
     32 	ZlibFDICT = 1 << 5,
     33 	ZlibFCHK  = 31 - (((ZlibCM | ZlibCINFO) << 8) | ZlibFLEV) % 31
     34 };
     35 
     36 int deflate_zlib_header(uchar *p, int n) {
     37 	if (n < 2)
     38 		return FlateErr;
     39 	p[0] = ZlibCM | ZlibCINFO;  /* deflate method, 32K window size */
     40 	p[1] = ZlibFLEV | ZlibFCHK; /* highest compression */
     41 	return 2;
     42 }
     43 
     44 int deflate_zlib_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
     45 	if (n < 4)
     46 		return FlateErr;
     47 	set32(p, sum);
     48 	return 4;
     49 }
     50 
     51 int inflate_zlib_header(uchar *p, int n) {
     52 	if (n < 2)
     53 		return FlateErr;
     54 	if (((p[0] << 8) | p[1]) % 31)
     55 		return FlateErr;
     56 	if ((p[0] & 0xf0) != ZlibCM || (p[0] & 0x0f) > ZlibCINFO)
     57 		return FlateErr;
     58 	if (p[1] & ZlibFDICT)
     59 		return FlateErr;
     60 	return 2;
     61 }
     62 
     63 int inflate_zlib_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
     64 	if (n < 4 || !check32(p, sum))
     65 		return FlateErr;
     66 	return 4;
     67 }
     68 
     69 
     70 enum {
     71 	GZipID1    = 0x1f,
     72 	GZipID2    = 0x8b,
     73 	GZipCM     = 8,
     74 	GZipFHCRC  = 1 << 1,
     75 	GZipFEXTRA = 1 << 2,
     76 	GZipFNAME  = 1 << 3,
     77 	GZipFCOMM  = 1 << 4,
     78 	GZipXFL    = 2,
     79 	GZipOS     = 255
     80 };
     81 
     82 int deflate_gzip_header(uchar *p, int n) {
     83 	if (n < 10)
     84 		return FlateErr;
     85 	memset(p, 0, 10);
     86 	p[0] = GZipID1;
     87 	p[1] = GZipID2;
     88 	p[2] = GZipCM;
     89 	p[8] = GZipXFL;
     90 	p[9] = GZipOS;
     91 	return 10;
     92 }
     93 
     94 int deflate_gzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
     95 	if (n < 8)
     96 		return FlateErr;
     97 	set32le(p, sum);
     98 	set32le(p+4, len);
     99 	return 8;
    100 }
    101 
    102 int inflate_gzip_header(uchar *p, int n) {
    103 	int k = 10;
    104 
    105 	if (k > n)
    106 		return FlateErr;
    107 	if (p[0] != GZipID1 || p[1] != GZipID2 || p[2] != GZipCM)
    108 		return FlateErr;
    109 	if (p[3] & GZipFEXTRA) {
    110 		k += 2 + ((p[k] << 8) | p[k+1]);
    111 		if (k > n)
    112 			return FlateErr;
    113 	}
    114 	if (p[3] & GZipFNAME) {
    115 		for (; k < n; k++)
    116 			if (p[k] == 0)
    117 				break;
    118 		k++;
    119 		if (k > n)
    120 			return FlateErr;
    121 	}
    122 	if (p[3] & GZipFCOMM) {
    123 		for (; k < n; k++)
    124 			if (p[k] == 0)
    125 				break;
    126 		k++;
    127 		if (k > n)
    128 			return FlateErr;
    129 	}
    130 	if (p[3] & GZipFHCRC) {
    131 		k += 2;
    132 		if (k > n)
    133 			return FlateErr;
    134 	}
    135 	return k;
    136 }
    137 
    138 int inflate_gzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
    139 	if (n < 8 || !check32le(p, sum) || !check32le(p+4, len))
    140 		return FlateErr;
    141 	return 8;
    142 }
    143 
    144 
    145 static char pkname[] = "sflate_stream";
    146 
    147 enum {
    148 	PKHeadID   = 0x04034b50,
    149 	PKDataID   = 0x08074b50,
    150 	PKDirID    = 0x02014b50,
    151 	PKFootID   = 0x06054b50,
    152 	PKVersion  = 20,
    153 	PKFlag     = 1 << 3,
    154 	PKMethod   = 8,
    155 	PKDate     = ((2009 - 1980) << 25) | (1 << 21) | (1 << 16),
    156 	PKHeadSize = 30,
    157 	PKDirSize  = 46,
    158 	PKNameLen  = sizeof(pkname) - 1
    159 };
    160 
    161 int deflate_pkzip_header(uchar *p, int n) {
    162 	if (n < PKHeadSize + PKNameLen)
    163 		return FlateErr;
    164 	memset(p, 0, PKHeadSize);
    165 	set32le(p, PKHeadID);
    166 	set32le(p+4, PKVersion);
    167 	set32le(p+6, PKFlag);
    168 	set32le(p+8, PKMethod);
    169 	set32le(p+10, PKDate);
    170 	set32le(p+26, PKNameLen);
    171 	memcpy(p + PKHeadSize, pkname, PKNameLen);
    172 	return PKHeadSize + PKNameLen;
    173 }
    174 
    175 int deflate_pkzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
    176 	if (n < PKDirSize + PKNameLen + 22)
    177 		return FlateErr;
    178 	/* unzip bug */
    179 /*
    180 	if (n < 16 + PKDirSize + PKNameLen + 22)
    181 		return FlateErr;
    182 	set32le(p, PKDataID);
    183 	set32le(p+4, sum);
    184 	set32le(p+8, zlen);
    185 	set32le(p+12, len);
    186 	p += 16;
    187 */
    188 	memset(p, 0, PKDirSize);
    189 	set32le(p, PKDirID);
    190 	set32le(p+4, PKVersion | (PKVersion << 16));
    191 	set32le(p+8, PKFlag);
    192 	set32le(p+10, PKMethod);
    193 	set32le(p+12, PKDate);
    194 	set32le(p+16, sum);
    195 	set32le(p+20, zlen);
    196 	set32le(p+24, len);
    197 	set32le(p+28, PKNameLen);
    198 	memcpy(p + PKDirSize, pkname, PKNameLen);
    199 	p += PKDirSize + PKNameLen;
    200 	memset(p, 0, 22);
    201 	set32le(p, PKFootID);
    202 	p[8] = p[10] = 1;
    203 	set32le(p+12, PKDirSize + PKNameLen);
    204 	set32le(p+16, zlen + PKHeadSize + PKNameLen);
    205 	return PKDirSize + PKNameLen + 22;
    206 /*
    207 	set32le(p+12, 16 + PKDirSize + PKNameLen);
    208 	set32le(p+16, zlen + PKHeadSize + PKNameLen);
    209 	return 16 + PKDirSize + PKNameLen + 22;
    210 */
    211 }
    212 
    213 int inflate_pkzip_header(uchar *p, int n) {
    214 	int k = 30;
    215 
    216 	if (k > n)
    217 		return FlateErr;
    218 	if (!check32le(p, PKHeadID))
    219 		return FlateErr;
    220 	if ((p[4] | (p[5] << 8)) > PKVersion)
    221 		return FlateErr;
    222 	if ((p[8] | (p[9] << 8)) != PKMethod)
    223 		return FlateErr;
    224 	k += p[26] | (p[27] << 8);
    225 	k += p[28] | (p[29] << 8);
    226 	if (k > n)
    227 		return FlateErr;
    228 	return k;
    229 }
    230 
    231 int inflate_pkzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) {
    232 	int k = PKDirSize + 22;
    233 
    234 	if (k > n)
    235 		return FlateErr;
    236 	if (check32le(p, PKDataID)) {
    237 		p += 16;
    238 		k += 16;
    239 		if (k > n)
    240 			return FlateErr;
    241 	}
    242 	if (!check32le(p, PKDirID))
    243 		return FlateErr;
    244 	if (!check32le(p+16, sum))
    245 		return FlateErr;
    246 	if (!check32le(p+20, zlen))
    247 		return FlateErr;
    248 	if (!check32le(p+24, len))
    249 		return FlateErr;
    250 	return k;
    251 }
    252 
    253 
    254 /* example usage */
    255 
    256 static int (*header)(uchar *, int);
    257 static int (*footer)(uchar *, int, uint, uint, uint);
    258 static uint (*checksum)(uchar *, int, uint);
    259 static char *err;
    260 static uint sum;
    261 static uint nin;
    262 static uint nout;
    263 static uint headerlen;
    264 static uint footerlen;
    265 static uint extralen;
    266 
    267 static int dummyheader(uchar *p, int n) {
    268 	return 0;
    269 }
    270 static int dummyfooter(uchar *p, int n, uint sum, uint len, uint zlen) {
    271 	return 0;
    272 }
    273 static uint dummysum(uchar *p, int n, uint sum) {
    274 	return 0;
    275 }
    276 
    277 /* compress, using FlateStream interface */
    278 int compress_stream(FILE *in, FILE *out) {
    279 	FlateStream s;
    280 	int k, n;
    281 	enum {Nin = 1<<15, Nout = 1<<15};
    282 
    283 	s.in = malloc(Nin);
    284 	s.out = malloc(Nout);
    285 	s.nin = 0;
    286 	s.nout = Nout;
    287 	s.err = 0;
    288 	s.state = 0;
    289 
    290 	k = header(s.out, s.nout);
    291 	if (k == FlateErr) {
    292 		s.err = "header error.";
    293 		n = FlateErr;
    294 	} else {
    295 		headerlen = s.nout = k;
    296 		n = FlateOut;
    297 	}
    298 	for (;; n = deflate(&s))
    299 		switch (n) {
    300 		case FlateOk:
    301 			k = footer(s.out, s.nout, sum, nin, nout - headerlen);
    302 			if (k == FlateErr) {
    303 				s.err = "footer error.";
    304 				n = FlateErr;
    305 			} else if (k != fwrite(s.out, 1, k, out)) {
    306 				s.err = "write error.";
    307 				n = FlateErr;
    308 			} else {
    309 				footerlen = k;
    310 				nout += k;
    311 			}
    312 		case FlateErr:
    313 			free(s.in);
    314 			free(s.out);
    315 			err = s.err;
    316 			return n;
    317 		case FlateIn:
    318 			s.nin = fread(s.in, 1, Nin, in);
    319 			nin += s.nin;
    320 			sum = checksum(s.in, s.nin, sum);
    321 			break;
    322 		case FlateOut:
    323 			k = fwrite(s.out, 1, s.nout, out);
    324 			if (k != s.nout)
    325 				s.err = "write error.";
    326 			nout += k;
    327 			s.nout = Nout;
    328 			break;
    329 		}
    330 }
    331 
    332 /* decompress, using FlateStream interface */
    333 int decompress_stream(FILE *in, FILE *out) {
    334 	FlateStream s;
    335 	uchar *begin;
    336 	int k, n;
    337 	enum {Nin = 1<<15, Nout = 1<<15};
    338 
    339 	s.in = begin = malloc(Nin);
    340 	s.out = malloc(Nout);
    341 	s.nout = Nout;
    342 	s.err = 0;
    343 	s.state = 0;
    344 
    345 	s.nin = fread(s.in, 1, Nin, in);
    346 	nin += s.nin;
    347 	k = header(s.in, s.nin);
    348 	if (k == FlateErr) {
    349 		s.err = "header error.";
    350 		n = FlateErr;
    351 	} else {
    352 		headerlen = k;
    353 		s.nin -= k;
    354 		s.in += k;
    355 		n = inflate(&s);
    356 	}
    357 	for (;; n = inflate(&s))
    358 		switch (n) {
    359 		case FlateOk:
    360 			memmove(begin, s.in, s.nin);
    361 			k = fread(begin, 1, Nin-s.nin, in);
    362 			nin += k;
    363 			s.nin += k;
    364 			k = footer(begin, s.nin, sum, nout, nin - s.nin - headerlen);
    365 			if (k == FlateErr) {
    366 				s.err = "footer error.";
    367 				n = FlateErr;
    368 			} else {
    369 				footerlen = k;
    370 				extralen = s.nin - k;
    371 			}
    372 		case FlateErr:
    373 			free(begin);
    374 			free(s.out);
    375 			err = s.err;
    376 			return n;
    377 		case FlateIn:
    378 			s.in = begin;
    379 			s.nin = fread(s.in, 1, Nin, in);
    380 			nin += s.nin;
    381 			break;
    382 		case FlateOut:
    383 			k = fwrite(s.out, 1, s.nout, out);
    384 			if (k != s.nout)
    385 				s.err = "write error.";
    386 			sum = checksum(s.out, k, sum);
    387 			nout += k;
    388 			s.nout = Nout;
    389 			break;
    390 		}
    391 }
    392 
    393 int main(int argc, char *argv[]) {
    394 	char comp = 'c';
    395 	char fmt = 'r';
    396 	char verbose = 'q';
    397 	int (*call)(FILE *, FILE*);
    398 	int n, i;
    399 
    400 	for (i = 1; i < argc; i++) {
    401 		if (argv[i][0] == '-' && argv[i][1] && argv[i][2] == 0)
    402 			switch (argv[i][1]) {
    403 			case 'q':
    404 			case 'v':
    405 				verbose = argv[i][1];
    406 				continue;
    407 			case 'c':
    408 			case 'd':
    409 				comp = argv[i][1];
    410 				continue;
    411 			case 'r':
    412 			case 'g':
    413 			case 'z':
    414 			case 'p':
    415 				fmt = argv[i][1];
    416 				continue;
    417 			}
    418 		fprintf(stderr, "usage: %s [-q|-v] [-c|-d] [-r|-g|-z|-p]\n\n"
    419 			"deflate stream compression\n"
    420 			" -q quiet (default)\n"
    421 			" -v verbose\n"
    422 			" -c compress (default)\n"
    423 			" -d decompress\n"
    424 			" -r raw (default)\n"
    425 			" -g gzip\n"
    426 			" -z zlib\n"
    427 			" -p pkzip\n", argv[0]);
    428 		return -1;
    429 	}
    430 	call = comp == 'c' ? compress_stream : decompress_stream;
    431 	switch (fmt) {
    432 	case 'r':
    433 		header = dummyheader;
    434 		footer = dummyfooter;
    435 		checksum = dummysum;
    436 		n = call(stdin, stdout);
    437 		break;
    438 	case 'g':
    439 		if (comp == 'c') {
    440 			header = deflate_gzip_header;
    441 			footer = deflate_gzip_footer;
    442 		} else {
    443 			header = inflate_gzip_header;
    444 			footer = inflate_gzip_footer;
    445 		}
    446 		checksum = crc32;
    447 		crc32init();
    448 		n = call(stdin, stdout);
    449 		break;
    450 	case 'z':
    451 		if (comp == 'c') {
    452 			header = deflate_zlib_header;
    453 			footer = deflate_zlib_footer;
    454 		} else {
    455 			header = inflate_zlib_header;
    456 			footer = inflate_zlib_footer;
    457 		}
    458 		checksum = adler32;
    459 		n = call(stdin, stdout);
    460 		break;
    461 	case 'p':
    462 		if (comp == 'c') {
    463 			header = deflate_pkzip_header;
    464 			footer = deflate_pkzip_footer;
    465 		} else {
    466 			header = inflate_pkzip_header;
    467 			footer = inflate_pkzip_footer;
    468 		}
    469 		checksum = crc32;
    470 		crc32init();
    471 		n = call(stdin, stdout);
    472 		break;
    473 	default:
    474 		err = "uninplemented.";
    475 		n = FlateErr;
    476 		break;
    477 	}
    478 	if (verbose == 'v')
    479 		fprintf(stderr, "in:%d out:%d checksum: 0x%08x (header:%d data:%d footer:%d extra input:%s)\n",
    480 			nin, nout, sum, headerlen, (comp == 'c' ? nout : nin) - headerlen - footerlen - extralen,
    481 			footerlen, extralen ? "yes" : "no");
    482 	if (n != FlateOk)
    483 		fprintf(stderr, "error: %s\n", err);
    484 	return n;
    485 }