view src/dmzlib.c @ 951:1723ebe6771c

Add kludge stbi__err() implementation temporarily until the error handling in dmzlib module is sorted out properly.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 26 Feb 2015 18:38:14 +0200
parents 6ed9465f3913
children 88cbea0ee9b5
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 STBI_ASSERT(x) // dummy


static int stbi__err(const char *s, const char *p)
{
    (void) s;
    (void) p;
    return 0;
}


// @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)
{
    STBI_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 * z, Uint8 * sizelist, int num)
{
    int i, k = 0;
    int code, next_code[16], sizes[17];

    // DEFLATE spec for generating codes
    memset(sizes, 0, sizeof(sizes));
    memset(z->fast, 0, sizeof(z->fast));

    for (i = 0; i < num; i++)
        sizes[sizelist[i]]++;

    sizes[0] = 0;
    for (i = 1; i < 16; i++)
        STBI_ASSERT(sizes[i] <= (1 << i));

    code = 0;
    for (i = 1; i < 16; i++)
    {
        next_code[i] = code;
        z->firstCode[i] = (Uint16) code;
        z->firstSymbol[i] = (Uint16) k;
        code = (code + sizes[i]);
        if (sizes[i])
            if (code - 1 >= (1 << i))
                return stbi__err("bad codelengths", "Corrupt JPEG");
        z->maxCode[i] = code << (16 - i);       // preshift for inner loop
        code <<= 1;
        k += sizes[i];
    }

    z->maxCode[16] = 0x10000;   // sentinel
    for (i = 0; i < num; i++)
    {
        int s = sizelist[i];
        if (s)
        {
            int c = next_code[s] - z->firstCode[s] + z->firstSymbol[s];
            Uint16 fastv = (Uint16) ((s << 9) | i);
            z->size[c] = (Uint8) s;
            z->value[c] = (Uint16) i;
            if (s <= STBI__ZFAST_BITS)
            {
                int k = stbi__bit_reverse_n(next_code[s], s);
                while (k < STBI__ZFAST_SIZE)
                {
                    z->fast[k] = fastv;
                    k += (1 << s);
                }
            }
            ++next_code[s];
        }
    }
    return 1;
}


static inline Uint8 stbi__zget8(DMZLibContext * z)
{
    if (z->zbuffer >= z->zbuffer_end)
        return 0;

    return *z->zbuffer++;
}


static void stbi__fill_bits(DMZLibContext * z)
{
    do
    {
        STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
        z->code_buffer |= stbi__zget8(z) << z->num_bits;
        z->num_bits += 8;
    }
    while (z->num_bits <= 24);
}


static inline unsigned int stbi__zreceive(DMZLibContext * z, int n)
{
    unsigned int k;
    if (z->num_bits < n)
        stbi__fill_bits(z);
    k = z->code_buffer & ((1 << n) - 1);
    z->code_buffer >>= n;
    z->num_bits -= n;
    return k;
}


static int stbi__zhuffman_decode_slowpath(DMZLibContext * a, DMZHuffmanContext * z)
{
    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(a->code_buffer);
    for (s = STBI__ZFAST_BITS + 1; ; s++)
    {
        if (k < z->maxCode[s]) break;
    }

    if (s == 16)
        return -1;              // invalid code!

    // code size is s, so:
    b = (k >> (16 - s)) - z->firstCode[s] + z->firstSymbol[s];
    STBI_ASSERT(z->size[b] == s);
    a->code_buffer >>= s;
    a->num_bits -= s;
    return z->value[b];
}


static inline int stbi__zhuffman_decode(DMZLibContext * a, DMZHuffmanContext * z)
{
    int b;
    if (a->num_bits < 16)
        stbi__fill_bits(a);

    b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
    if (b)
    {
        int s = b >> 9;
        a->code_buffer >>= s;
        a->num_bits -= s;
        return b & 511;
    }
    return stbi__zhuffman_decode_slowpath(a, z);
}


static int stbi__zexpand(DMZLibContext * z, char *zout, int n)     // need to make room for n bytes
{
    char *q;
    int cur, limit;
    z->zout = zout;

    if (!z->z_expandable)
        return stbi__err("output buffer limit", "Corrupt PNG");

    cur = (int) (z->zout - z->zout_start);
    limit = (int) (z->zout_end - z->zout_start);

    while (cur + n > limit)
        limit *= 2;

    if ((q = (char *) dmRealloc(z->zout_start, limit)) == NULL)
        return stbi__err("outofmem", "Out of memory");

    z->zout_start = q;
    z->zout = q + cur;
    z->zout_end = q + limit;
    return 1;
}


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)
{
    char *zout = a->zout;
    for (;;)
    {
        int z = stbi__zhuffman_decode(a, &a->z_length);
        if (z < 256)
        {
            if (z < 0)
                return stbi__err("bad huffman code", "Corrupt PNG");    // error in huffman codes

            if (zout >= a->zout_end)
            {
                if (!stbi__zexpand(a, zout, 1))
                    return 0;
                zout = a->zout;
            }
            *zout++ = (char) z;
        }
        else
        {
            Uint8 *p;
            int len, dist;
            if (z == 256)
            {
                a->zout = zout;
                return 1;
            }
            z -= 257;

            len = stbi__zlength_base[z];
            if (stbi__zlength_extra[z])
                len += stbi__zreceive(a, stbi__zlength_extra[z]);

            z = stbi__zhuffman_decode(a, &a->z_distance);
            if (z < 0)
                return stbi__err("bad huffman code", "Corrupt PNG");

            dist = stbi__zdist_base[z];
            if (stbi__zdist_extra[z])
                dist += stbi__zreceive(a, stbi__zdist_extra[z]);

            if (zout - a->zout_start < dist)
                return stbi__err("bad dist", "Corrupt PNG");

            if (zout + len > a->zout_end)
            {
                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;

    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 (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19))
        return 0;

    n = 0;
    while (n < hlit + hdist)
    {
        int c = stbi__zhuffman_decode(a, &z_codelength);
        STBI_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 stbi__err("bad codelengths", "Corrupt PNG");

    if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit))
        return 0;

    if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist))
        return 0;
    return 1;
}


static int stbi__parse_uncompressed_block(DMZLibContext * a)
{
    Uint8 header[4];
    int len, nlen, k;

    if (a->num_bits & 7)
        stbi__zreceive(a, a->num_bits & 7);     // discard

    // drain the bit-packed data into header
    k = 0;
    while (a->num_bits > 0)
    {
        header[k++] = (Uint8) (a->code_buffer & 255);   // suppress MSVC run-time check
        a->code_buffer >>= 8;
        a->num_bits -= 8;
    }
    STBI_ASSERT(a->num_bits == 0);

    // now fill header the normal way
    while (k < 4)
        header[k++] = stbi__zget8(a);

    len = header[1] * 256 + header[0];
    nlen = header[3] * 256 + header[2];

    if (nlen != (len ^ 0xffff))
        return stbi__err("zlib corrupt", "Corrupt PNG");

    if (a->zbuffer + len > a->zbuffer_end)
        return stbi__err("read past buffer", "Corrupt PNG");

    if (a->zout + len > a->zout_end && !stbi__zexpand(a, a->zout, len))
        return 0;

    memcpy(a->zout, a->zbuffer, len);
    a->zbuffer += len;
    a->zout += len;
    return 1;
}


static int stbi__parse_zlib_header(DMZLibContext * a)
{
    int cmf = stbi__zget8(a);
    int cm = cmf & 15;
    int flg = stbi__zget8(a);
    // int cinfo = cmf >> 4;

    if ((cmf * 256 + flg) % 31 != 0)
        return stbi__err("bad zlib header", "Corrupt PNG");     // zlib spec

    if (flg & 32)
        return stbi__err("no preset dict", "Corrupt PNG");      // preset dictionary not allowed in png

    if (cm != 8)
        return stbi__err("bad compression", "Corrupt PNG");     // DEFLATE required for png

    // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
    return 1;
}


static int stbi__parse_zlib(DMZLibContext * a, BOOL parse_header)
{
    int final, type;

    if (parse_header && !stbi__parse_zlib_header(a))
        return 0;

    a->num_bits = 0;
    a->code_buffer = 0;
    do
    {
        final = stbi__zreceive(a, 1);
        type = stbi__zreceive(a, 2);
        if (type == 0)
        {
            if (!stbi__parse_uncompressed_block(a))
                return 0;
        }
        else
        if (type == 3)
            return 0;
        else
        {
            if (type == 1)
            {
                // use fixed code lengths
                if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288))
                    return 0;

                if (!stbi__zbuild_huffman
                    (&a->z_distance, stbi__zdefault_distance, 32))
                    return 0;
            }
            else
            {
                if (!stbi__compute_huffman_codes(a))
                    return 0;
            }
            if (!stbi__parse_huffman_block(a))
                return 0;
        }
    }
    while (!final);
    return 1;
}


static int stbi__do_zlib(DMZLibContext * a, char *obuf, int olen, int exp,
                         BOOL parse_header)
{
    a->zout_start = obuf;
    a->zout = obuf;
    a->zout_end = obuf + olen;
    a->z_expandable = exp;

    return stbi__parse_zlib(a, parse_header);
}


char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
{
    DMZLibContext a;
    char *p = (char *) dmMalloc(initial_size);
    if (p == NULL)
        return NULL;
    a.zbuffer = (Uint8 *) buffer;
    a.zbuffer_end = (Uint8 *) buffer + len;
    if (stbi__do_zlib(&a, p, initial_size, 1, 1))
    {
        if (outlen)
            *outlen = (int) (a.zout - a.zout_start);
        return a.zout_start;
    }
    else
    {
        dmFree(a.zout_start);
        return NULL;
    }
}


char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
{
    return stbi_zlib_decode_malloc_guesssize(buffer, len, DM_ZLIB_TMPBUF_SIZE, outlen);
}


char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer,
   int len, int initial_size, int *outlen, BOOL parse_header)
{
    DMZLibContext a;
    char *p = (char *) dmMalloc(initial_size);
    if (p == NULL)
        return NULL;

    a.zbuffer = (Uint8 *) buffer;
    a.zbuffer_end = (Uint8 *) buffer + len;
    if (stbi__do_zlib(&a, p, initial_size, 1, parse_header))
    {
        if (outlen)
            *outlen = (int) (a.zout - a.zout_start);
        return a.zout_start;
    }
    else
    {
        dmFree(a.zout_start);
        return NULL;
    }
}


int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
{
    DMZLibContext a;
    a.zbuffer = (Uint8 *) ibuffer;
    a.zbuffer_end = (Uint8 *) ibuffer + ilen;
    if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
        return (int) (a.zout - a.zout_start);
    else
        return -1;
}


char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
{
    DMZLibContext a;
    char *p = (char *) dmMalloc(DM_ZLIB_TMPBUF_SIZE);
    if (p == NULL)
        return NULL;

    a.zbuffer = (Uint8 *) buffer;
    a.zbuffer_end = (Uint8 *) buffer + len;

    if (stbi__do_zlib(&a, p, DM_ZLIB_TMPBUF_SIZE, 1, 0))
    {
        if (outlen != NULL)
            *outlen = (int) (a.zout - a.zout_start);
        return a.zout_start;
    }
    else
    {
        dmFree(a.zout_start);
        return NULL;
    }
}


int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
{
    DMZLibContext a;

    a.zbuffer = (Uint8 *) ibuffer;
    a.zbuffer_end = (Uint8 *) ibuffer + ilen;

    if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
        return (int) (a.zout - a.zout_start);
    else
        return -1;
}