Mercurial > hg > dmlib
view src/dmzlib.c @ 958:985225a93aeb
Add error code parameter to dmError() and dmErrorVA().
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 27 Feb 2015 03:58:25 +0200 |
parents | 6b2f41844580 |
children | 1832ac20edb2 |
line wrap: on
line source
/* * DMLib * -- ZLib implementation * Public domain zlib decode v0.2 by Sean Barrett 2006-11-18 * Modified and reformatted for DMLib by Matti 'ccr' Hamalainen */ #include "dmzlib.h" #define DM_ZLIB_TMPBUF_SIZE (16 * 1024) #define DMSTBI_ASSERT(x) // dummy // @TODO: should statically initialize these for optimal thread safety static Uint8 stbi__zdefault_length[288], stbi__zdefault_distance[32]; void dmZLibInit() { int i; // use <= to match clearly with spec for (i = 0; i <= 143; i++) stbi__zdefault_length[i] = 8; for (; i <= 255; i++) stbi__zdefault_length[i] = 9; for (; i <= 279; i++) stbi__zdefault_length[i] = 7; for (; i <= 287; i++) stbi__zdefault_length[i] = 8; for (i = 0; i <= 31; i++) stbi__zdefault_distance[i] = 5; } static inline int stbi__bit_reverse_16(int n) { n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); return n; } static inline int stbi__bit_reverse_n(int v, int bits) { DMSTBI_ASSERT(bits <= 16); // to bit reverse n bits, reverse 16 and shift // e.g. 11 bits, bit reverse and shift away 5 return stbi__bit_reverse_16(v) >> (16 - bits); } static int stbi__zbuild_huffman(DMZHuffmanContext * ctx, const Uint8 * sizelist, const int num) { int i, k = 0; int code, next_code[16], sizes[17]; // DEFLATE spec for generating codes memset(sizes, 0, sizeof(sizes)); memset(ctx->fast, 0, sizeof(ctx->fast)); for (i = 0; i < num; i++) sizes[sizelist[i]]++; sizes[0] = 0; for (i = 1; i < 16; i++) { if (sizes[i] > (1 << i)) { return dmError(DMERR_INTERNAL, "Sizes assert failed while building Huffman codes.\n"); } } code = 0; for (i = 1; i < 16; i++) { next_code[i] = code; ctx->firstCode[i] = (Uint16) code; ctx->firstSymbol[i] = (Uint16) k; code = (code + sizes[i]); if (sizes[i] && code - 1 >= (1 << i)) { return dmError(DMERR_INVALID_DATA, "Bad Huffman code lengths.\n"); } ctx->maxCode[i] = code << (16 - i); // preshift for inner loop code <<= 1; k += sizes[i]; } ctx->maxCode[16] = 0x10000; // sentinel for (i = 0; i < num; i++) { int s = sizelist[i]; if (s) { int c = next_code[s] - ctx->firstCode[s] + ctx->firstSymbol[s]; Uint16 fastv = (Uint16) ((s << 9) | i); ctx->size[c] = (Uint8) s; ctx->value[c] = (Uint16) i; if (s <= STBI__ZFAST_BITS) { int k = stbi__bit_reverse_n(next_code[s], s); while (k < STBI__ZFAST_SIZE) { ctx->fast[k] = fastv; k += (1 << s); } } ++next_code[s]; } } return 1; } static inline Uint8 stbi__zget8(DMZLibContext * ctx) { if (ctx->zbuffer >= ctx->zbufferEnd) return 0; return *ctx->zbuffer++; } static void stbi__fill_bits(DMZLibContext * ctx) { do { DMSTBI_ASSERT(ctx->codeBuffer < (1U << ctx->numBits)); ctx->codeBuffer |= stbi__zget8(ctx) << ctx->numBits; ctx->numBits += 8; } while (ctx->numBits <= 24); } static inline unsigned int stbi__zreceive(DMZLibContext * ctx, int n) { unsigned int val; if (ctx->numBits < n) stbi__fill_bits(ctx); val = ctx->codeBuffer & ((1 << n) - 1); ctx->codeBuffer >>= n; ctx->numBits -= n; return val; } static int stbi__zhuffman_decode_slowpath(DMZLibContext * ctx, DMZHuffmanContext * huff, int *val) { int b, s, k; // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top k = stbi__bit_reverse_16(ctx->codeBuffer); for (s = STBI__ZFAST_BITS + 1; ; s++) { if (k < huff->maxCode[s]) break; } if (s == 16) { return dmError(DMERR_DATA_ERROR, "Bad Huffman code.\n"); } // code size is s, so: b = (k >> (16 - s)) - huff->firstCode[s] + huff->firstSymbol[s]; DMSTBI_ASSERT(huff->size[b] == s); ctx->codeBuffer >>= s; ctx->numBits -= s; *val = huff->value[b]; return DMERR_OK; } static inline int stbi__zhuffman_decode(DMZLibContext * ctx, DMZHuffmanContext * huff, int *val) { int b; if (ctx->numBits < 16) stbi__fill_bits(ctx); b = huff->fast[ctx->codeBuffer & STBI__ZFAST_MASK]; if (b) { int s = b >> 9; ctx->codeBuffer >>= s; ctx->numBits -= s; *val = b & 511; return DMERR_OK; } return stbi__zhuffman_decode_slowpath(ctx, huff, val); } static int stbi__zexpand(DMZLibContext * ctx, Uint8 *zout, size_t n) { Uint8 *newBuf; size_t cur, limit; ctx->zout = zout; if (!ctx->expandable) { return dmError(DMERR_BOUNDS, "Output buffer limit hit, and is not expandable.\n"); } cur = ctx->zout - ctx->zoutStart; limit = ctx->zoutEnd - ctx->zoutStart; while (cur + n > limit) limit *= 2; if ((newBuf = (Uint8 *) dmRealloc(ctx->zoutStart, limit)) == NULL) { return dmError(DMERR_MALLOC, "Could not reallocate buffer.\n"); } ctx->zoutStart = newBuf; ctx->zout = newBuf + cur; ctx->zoutEnd = newBuf + limit; return DMERR_OK; } static const int stbi__zlength_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; static const int stbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; static const int stbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; static const int stbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static int stbi__parse_huffman_block(DMZLibContext * a) { Uint8 *zout = a->zout; for (;;) { int z, ret; if ((ret = stbi__zhuffman_decode(a, &a->zlength, &z)) != DMERR_OK) return ret; if (z < 256) { if (zout >= a->zoutEnd) { if ((ret = stbi__zexpand(a, zout, 1)) != DMERR_OK) return ret; zout = a->zout; } *zout++ = (Uint8) z; } else { Uint8 *p; int len, dist; if (z == 256) { a->zout = zout; return DMERR_OK; } z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); if ((ret = stbi__zhuffman_decode(a, &a->zdistance, &z)) != DMERR_OK) return ret; dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zoutStart < dist) { return dmError(DMERR_DATA_ERROR, "Bad Huffman block distance.\n"); } if (zout + len > a->zoutEnd) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } p = (Uint8 *) (zout - dist); if (dist == 1) { // run of one byte; common in images. Uint8 v = *p; do { *zout++ = v; } while (--len); } else { do { *zout++ = *p++; } while (--len); } } } } static const Uint8 length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6 , 10, 5 , 11, 4, 12, 3, 13, 2 , 14, 1 , 15 }; static int stbi__compute_huffman_codes(DMZLibContext * a) { DMZHuffmanContext z_codelength; Uint8 lencodes[286 + 32 + 137]; //padding for maximum single op Uint8 codelength_sizes[19]; int i, n, ret; int hlit = stbi__zreceive(a, 5) + 257; int hdist = stbi__zreceive(a, 5) + 1; int hclen = stbi__zreceive(a, 4) + 4; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i = 0; i < hclen; i++) { int s = stbi__zreceive(a, 3); codelength_sizes[length_dezigzag[i]] = (Uint8) s; } if ((ret = stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) != DMERR_OK) return ret; n = 0; while (n < hlit + hdist) { int c; if ((ret = stbi__zhuffman_decode(a, &z_codelength, &c)) != DMERR_OK) return ret; DMSTBI_ASSERT(c >= 0 && c < 19); if (c < 16) lencodes[n++] = (Uint8) c; else if (c == 16) { c = stbi__zreceive(a, 2) + 3; memset(lencodes + n, lencodes[n - 1], c); n += c; } else if (c == 17) { c = stbi__zreceive(a, 3) + 3; memset(lencodes + n, 0, c); n += c; } else { c = stbi__zreceive(a, 7) + 11; memset(lencodes + n, 0, c); n += c; } } if (n != hlit + hdist) { return dmError(DMERR_DATA_ERROR, "Bad huffman codelengths.\n"); } if ((ret = stbi__zbuild_huffman(&a->zlength, lencodes, hlit)) != DMERR_OK) return ret; if ((ret = stbi__zbuild_huffman(&a->zdistance, lencodes + hlit, hdist)) != DMERR_OK) return ret; return DMERR_OK; } static int stbi__parse_uncompressed_block(DMZLibContext * a) { Uint8 header[4]; int len, nlen, k; if (a->numBits & 7) stbi__zreceive(a, a->numBits & 7); // discard // drain the bit-packed data into header k = 0; while (a->numBits > 0) { header[k++] = (Uint8) (a->codeBuffer & 255); // suppress MSVC run-time check a->codeBuffer >>= 8; a->numBits -= 8; } DMSTBI_ASSERT(a->numBits == 0); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = (header[1] << 8) | header[0]; nlen = (header[3] << 8) | header[2]; if (nlen != (len ^ 0xffff)) { return dmError(DMERR_DATA_ERROR, "Compressed data corrupt.\n"); } if (a->zbuffer + len > a->zbufferEnd) { return dmError(DMERR_BOUNDS, "Read past buffer, probably corrupt compressed data.\n"); } if (a->zout + len > a->zoutEnd && !stbi__zexpand(a, a->zout, len)) { return dmError(DMERR_DATA_ERROR, "XXXX TODO"); } memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; return DMERR_OK; } int dmZLibParseHeader(DMZLibContext * ctx, BOOL checkPNG) { int cmf = stbi__zget8(ctx); int flags = stbi__zget8(ctx); int cm = cmf & 15; // int cinfo = cmf >> 4; if ((cmf * 256 + flags) % 31 != 0) { return dmError(DMERR_INVALID_DATA, "Bad zlib header."); } if (checkPNG && (flags & 32)) { // preset dictionary not allowed in png return dmError(DMERR_NOT_SUPPORTED, "Preset dictionary not allowed in PNG.\n"); } if (checkPNG && cm != 8) { return dmError(DMERR_INVALID_DATA, "Bad or unsupported compression type.\n"); } // window = 1 << (8 + cinfo)... but who cares, we fully buffer output return DMERR_OK; } int dmZLibDecode(DMZLibContext * ctx) { int final, type, ret; ctx->numBits = 0; ctx->codeBuffer = 0; do { final = stbi__zreceive(ctx, 1); type = stbi__zreceive(ctx, 2); if (type == 0) { if ((ret = stbi__parse_uncompressed_block(ctx)) != DMERR_OK) return ret; } else if (type == 3) return 0; else { if (type == 1) { // use fixed code lengths if ((ret = stbi__zbuild_huffman(&ctx->zlength, stbi__zdefault_length, 288)) != DMERR_OK) return ret; if ((ret = stbi__zbuild_huffman(&ctx->zdistance, stbi__zdefault_distance, 32)) != DMERR_OK) return ret; } else if ((ret = stbi__compute_huffman_codes(ctx)) != DMERR_OK) return ret; if ((ret = stbi__parse_huffman_block(ctx)) != DMERR_OK) return ret; } } while (!final); return DMERR_OK; } Uint8 *stbi_zlib_decode_malloc_guesssize_headerflag( const Uint8 *buffer, const size_t len, const size_t initialSize, size_t *outLen, BOOL parseHeader) { DMZLibContext ctx; Uint8 *outBuf; int ret; if ((outBuf = dmMalloc(initialSize)) == NULL) return NULL; ctx.zbuffer = (Uint8 *) buffer; ctx.zbufferEnd = (Uint8 *) buffer + len; ctx.zout = outBuf; ctx.zoutStart = outBuf; ctx.zoutEnd = outBuf + initialSize; ctx.expandable = TRUE; if (parseHeader && (ret = dmZLibParseHeader(&ctx, TRUE)) != DMERR_OK) { return dmError(ret, "Failed to parse zlib header data.\n"); } if ((ret = dmZLibDecode(&ctx)) != DMERR_OK) { if (outLen) *outLen = ctx.zout - ctx.zoutStart; return ctx.zoutStart; } else { dmFree(ctx.zoutStart); return NULL; } } #if 0 Uint8 *stbi_zlib_decode_malloc(Uint8 const *buffer, int len, int *outlen) { return stbi_zlib_decode_malloc_guesssize(buffer, len, DM_ZLIB_TMPBUF_SIZE, outlen); } int stbi_zlib_decode_buffer(Uint8 *obuffer, size_t olen, Uint8 const *ibuffer, size_t ilen, size_t *res) { DMZLibContext ctx; int ret; ctx.zbuffer = (Uint8 *) ibuffer; ctx.zbufferEnd = (Uint8 *) ibuffer + ilen; if ((ret = stbi__do_zlib(&a, obuffer, olen, 0, 1)) != DMERR_OK) { *res = a.zout - a.zoutStart; return DMERR_OK; } else return ret; } Uint8 *stbi_zlib_decode_noheader_malloc(Uint8 const *buffer, int len, size_t *outlen) { DMZLibContext ctx; Uint8 *p = (Uint8 *) dmMalloc(DM_ZLIB_TMPBUF_SIZE); if (p == NULL) return NULL; ctx.zbuffer = (Uint8 *) buffer; ctx.zbufferEnd = (Uint8 *) buffer + len; if (stbi__do_zlib(&ctx, p, DM_ZLIB_TMPBUF_SIZE, 1, 0)) { if (outlen != NULL) *outlen = (int) (ctx.zout - a.zoutStart); return a.zoutStart; } else { dmFree(a.zoutStart); return NULL; } } int stbi_zlib_decode_noheader_buffer(Uint8 *obuffer, int olen, const Uint8 *ibuffer, int ilen) { DMZLibContext a; a.zbuffer = (Uint8 *) ibuffer; a.zbufferEnd = (Uint8 *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) return (int) (a.zout - a.zoutStart); else return -1; } #endif