Упаковка\распаковка zlib

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by kipovec, 27 Jul 2010.

  1. kipovec

    kipovec New Member

    Joined:
    21 Jul 2010
    Messages:
    1
    Likes Received:
    0
    Reputations:
    0
    Есть небольшая проблемка с zlib.
    Архивирую текстовый файл, вроде все нормально.
    При распаковке, распаковывается начало файла 200 - 3000 байт и вывыливается ошибка invalid or incomplete deflate data.
    Подскажите в чем может быть проблема, ну или хотя бы в какую сторону нужно смотреть.
    Исходник прилагается. За ранее спасибо.
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include "zlib.h"
    
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define CHUNK 16384
    
    /* Compress from file source to file dest until EOF on source.
       def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
       allocated for processing, Z_STREAM_ERROR if an invalid compression
       level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
       version of the library linked do not match, or Z_ERRNO if there is
       an error reading or writing the files. */
    int def(FILE *source, FILE *dest, int level)
    {
        int ret, flush;
        unsigned have;
        z_stream strm;
        char in[CHUNK];
        char out[CHUNK];
        char *buf;
        unsigned long dest_size;
        unsigned long indx = 0;
        struct stat sb;
    
        have = fileno(source);
    
        if (fstat(have, &sb) < 0)
            return Z_ERRNO;
    
        if (!S_ISREG(sb.st_mode))
            return Z_ERRNO;
    
        if ((dest_size = (unsigned long)sb.st_size) <= 0)
            return Z_ERRNO;
    
        if (fwrite(&dest_size, 4, 1, dest) != 1 || ferror(dest))
            return Z_ERRNO;
    
        /* allocate deflate state */
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        ret = deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
        if (ret != Z_OK)
            return ret;
    
        /* compress until end of file */
        do {
            strm.avail_in = fread(in, 1, CHUNK, source);
            if (ferror(source)) {
                (void)deflateEnd(&strm);
                return Z_ERRNO;
            }
            flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
            strm.next_in = in;
    
            /* run deflate() on input until output buffer not full, finish
               compression if all of source has been read in */
            do {
                strm.avail_out = CHUNK;
                strm.next_out = out;
                ret = deflate(&strm, flush);    /* no bad return value */
                assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
                have = CHUNK - strm.avail_out;
                if (have > 0) {
                    buf = out;
    
                    do {
                        if (dest_size > indx++) {
                            switch (indx) {
                            case 1:
                                *buf ^= 0x92;
                                break;
                            case 2:
                                *buf ^= 0x65;
                                break;
                            case 3:
                                *buf ^= 0x67;
                                break;
                            case 4:
                                *buf ^= 0x57;
                            }
                        }
                    } while (*buf++);
    
                    if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
                        (void)deflateEnd(&strm);
                        return Z_ERRNO;
                    }
                }
            } while (strm.avail_out == 0);
            assert(strm.avail_in == 0);     /* all input will be used */
    
            /* done when last data in file processed */
        } while (flush != Z_FINISH);
        assert(ret == Z_STREAM_END);        /* stream will be complete */
    
        /* clean up and return */
        (void)deflateEnd(&strm);
        return Z_OK;
    }
    
    /* Decompress from file source to file dest until stream ends or EOF.
       inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
       allocated for processing, Z_DATA_ERROR if the deflate data is
       invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
       the version of the library linked do not match, or Z_ERRNO if there
       is an error reading or writing the files. */
    int inf(FILE *source, FILE *dest)
    {
        int ret;
        unsigned have;
        z_stream strm;
        char in[CHUNK];
        char out[CHUNK];
        char *buf;
        unsigned long dest_size;
        unsigned long indx = 0;
    
        have = fread(in, 1, 4, source);
    
        if (have != 4 || ferror(source)) {
            return Z_ERRNO;
        }
    
        dest_size = ((unsigned long)in[0]) |
                    ((unsigned long)in[1] << 8) | 
                    ((unsigned long)in[2] << 16) | 
                    ((unsigned long)in[3] << 24);
    
        /* allocate inflate state */
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        strm.avail_in = 0;
        strm.next_in = Z_NULL;
        ret = inflateInit2(&strm, -MAX_WBITS);
        if (ret != Z_OK)
            return ret;
    
        /* decompress until deflate stream ends or end of file */
        do {
            strm.avail_in = fread(in, 1, CHUNK, source);
            if (ferror(source)) {
                (void)inflateEnd(&strm);
                return Z_ERRNO;
            }
            if (strm.avail_in == 0)
                break;
    
            buf = in;
    
            do {
                if (dest_size > indx++) {
                    switch (indx) {
                    case 1:
                        *buf ^= 0x92;
                        break;
                    case 2:
                        *buf ^= 0x65;
                        break;
                    case 3:
                        *buf ^= 0x67;
                        break;
                    case 4:
                        *buf ^= 0x57;
                    }
                }
            } while (*buf++);
    
            strm.next_in = in;
    
            /* run inflate() on input until output buffer not full */
            do {
                strm.avail_out = CHUNK;
                strm.next_out = out;
                ret = inflate(&strm, Z_NO_FLUSH);
                assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
                switch (ret) {
                case Z_NEED_DICT:
                    ret = Z_DATA_ERROR;     /* and fall through */
                case Z_DATA_ERROR:
                case Z_MEM_ERROR:
                    (void)inflateEnd(&strm);
                    return ret;
                }
                have = CHUNK - strm.avail_out;
                if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
                    (void)inflateEnd(&strm);
                    return Z_ERRNO;
                }
            } while (strm.avail_out == 0);
    
            /* done when inflate() says it's done */
        } while (ret != Z_STREAM_END);
    
        /* clean up and return */
        (void)inflateEnd(&strm);
        return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
    }
    
    /* report a zlib or i/o error */
    void zerr(int ret)
    {
        fputs("zpipe: ", stderr);
        switch (ret) {
        case Z_ERRNO:
            if (ferror(stdin))
                fputs("error reading stdin\n", stderr);
            if (ferror(stdout))
                fputs("error writing stdout\n", stderr);
            break;
        case Z_STREAM_ERROR:
            fputs("invalid compression level\n", stderr);
            break;
        case Z_DATA_ERROR:
            fputs("invalid or incomplete deflate data\n", stderr);
            break;
        case Z_MEM_ERROR:
            fputs("out of memory\n", stderr);
            break;
        case Z_VERSION_ERROR:
            fputs("zlib version mismatch!\n", stderr);
        }
    }
    
    /* compress or decompress from stdin to stdout */
    int main(int argc, char **argv)
    {
        int ret;
    
        /* do compression if -c specified */
        if (argc == 2 && strcmp(argv[1], "-c") == 0) {
            ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION);
            if (ret != Z_OK)
                zerr(ret);
            return ret;
        }
    
        /* do decompression if -d specified */
        else if (argc == 2 && strcmp(argv[1], "-d") == 0) {
            ret = inf(stdin, stdout);
            if (ret != Z_OK)
                zerr(ret);
            return ret;
        }
    
        /* otherwise, report usage */
        else {
            fputs("encpack usage: encpack [-c|-d] < source > dest\n", stderr);
            fputs("E.g: encpack -c < file.txt > file.enc\n\n", stderr);
            return 1;
        }
    }
    
     
  2. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2 kipovec
    что будет в dest_size если в in будет например следующее "\xff\x00\x00\x00" правильно 4294967295
    или полная ерунда
    Code:
    dest_size =
        ((unsigned long)in[0]) |
        ((unsigned long)in[1] << 8) | 
        ((unsigned long)in[2] << 16) | 
        ((unsigned long)in[3] << 24);
    
    знаковое расширение из char в unsigned long а потом
    когда уже результат неверный сдвиг и включение правильно написать можно так
    Code:
    dest_size =
        (ulong)((uchar*)in)[0] |
        (ulong)((uchar*)in)[1] << 8 | 
        (ulong)((uchar*)in)[2] << 16 | 
        (ulong)((uchar*)in)[3] << 24;
    
    или так
    Code:
    dest_size =
        ((ulong)in[0] & 0xff) |
        ((ulong)in[1] & 0xff) << 8 |
        ((ulong)in[2] & 0xff) << 16 |
        ((ulong)in[3] & 0xff) << 24;
    
    ну или надо было сразу массив in объявить как unsigned char
     
    #2 greki_hoy, 28 Jul 2010
    Last edited: 28 Jul 2010
  3. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    А зачем так мучатся? Если можно из винсока взять апишку htonl
    или заюзать макрос типа:
    Code:
    #define ntohl(x)(ULONG)((((ULONG)(x)<<24)&0xFF000000)^ \
                            (((ULONG)(x)<< 8)&0x00FF0000)^ \
                            (((ULONG)(x)>> 8)&0x0000FF00)^ \
                            (((ULONG)(x)>>24)&0x000000FF))
    
    
     
  4. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2 slesh
    Code:
    #define ntohl(x)(ULONG)((((ULONG)(x)<<24)&0xFF000000)^ \
                            (((ULONG)(x)<< 8)&0x00FF0000)^ \
                            (((ULONG)(x)>> 8)&0x0000FF00)^ \
                            (((ULONG)(x)>>24)&0x000000FF))
    
    макрос будет работать только для little endian тачек
    универсальная версия выглядела бы как то так
    Code:
    #define swap_32(x)                    \
    	(((x) & 0xff000000) >> 24 |   \
    	 ((x) & 0x00ff0000) >> 8 |    \
    	 ((x) & 0x0000ff00) << 8 |    \
    	 ((x) & 0x000000ff) << 24)
    
    ulong ntohl(ulong val)
    {
        ulong t = 0x1;
        if (*(uchar*)&t) /* если тачка little endian меняем порядок */
            return swap_32(val);
        return val; /* иначе тачка big endian порядок не трогаем */
    }
    
     
  5. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2 slesh
    хотя разработчики reactos'а вынесли в компайл тайм
    определение надо или не надо менять порядок байт
    Code:
    /*
     * COPYRIGHT:   See COPYING in the top level directory
     * PROJECT:     ReactOS WinSock 2 DLL
     * FILE:        include/ws2_32.h
     * PURPOSE:     WinSock 2 DLL header
     */
    #ifdef LE
    
    /* DWORD network to host byte order conversion for little endian machines */
    #define DN2H(dw) \
      ((((dw) & 0xFF000000L) >> 24) | \
       (((dw) & 0x00FF0000L) >> 8) | \
    	 (((dw) & 0x0000FF00L) << 8) | \
    	 (((dw) & 0x000000FFL) << 24))
    
    /* DWORD host to network byte order conversion for little endian machines */
    #define DH2N(dw) \
    	((((dw) & 0xFF000000L) >> 24) | \
    	 (((dw) & 0x00FF0000L) >> 8) | \
    	 (((dw) & 0x0000FF00L) << 8) | \
    	 (((dw) & 0x000000FFL) << 24))
    
    /* WORD network to host order conversion for little endian machines */
    #define WN2H(w) \
    	((((w) & 0xFF00) >> 8) | \
    	 (((w) & 0x00FF) << 8))
    
    /* WORD host to network byte order conversion for little endian machines */
    #define WH2N(w) \
    	((((w) & 0xFF00) >> 8) | \
    	 (((w) & 0x00FF) << 8))
    
    #else /* LE */
    
    /* DWORD network to host byte order conversion for big endian machines */
    #define DN2H(dw) \
        (dw)
    
    /* DWORD host to network byte order conversion big endian machines */
    #define DH2N(dw) \
        (dw)
    
    /* WORD network to host order conversion for big endian machines */
    #define WN2H(w) \
        (w)
    
    /* WORD host to network byte order conversion for big endian machines */
    #define WH2N(w) \
        (w)
    
    #endif /* LE */
    
    а потом
    Code:
     
    /*
     * COPYRIGHT:   See COPYING in the top level directory
     * PROJECT:     ReactOS WinSock 2 DLL
     * FILE:        misc/bsd.c
     * PURPOSE:     Legacy BSD sockets APIs
     * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
     * REVISIONS:
     *   CSH 15/06-2001 Created
     */
    #include <ws2_32.h>
    
    /*
     * @implemented
     */
    ULONG
    EXPORT
    htonl(IN ULONG hostlong)
    {
        return DH2N(hostlong);
    }
    
    
    /*
     * @implemented
     */
    USHORT
    EXPORT
    htons(IN USHORT hostshort)
    {
        return WH2N(hostshort);
    }
    
    
    /*
     * @implemented
     */
    ULONG
    EXPORT
    ntohl(IN ULONG netlong)
    {
        return DN2H(netlong);
    }
    
    
    /*
     * @implemented
     */
    USHORT
    EXPORT
    ntohs(IN USHORT netshort)
    {
        return WN2H(netshort);
    }
    
    /* EOF */