[C++] Zlib problemen

Pagina: 1
Acties:

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Nadat ik eem ge-gzipte string heb ontvangen probeer ik met zlib deze te inflaten. Normaal gesproken nooit echt een probleem geweest. Maar nu ontvang in van zlib steeds de error code -3. Ik lees 121 bytes van een bestand en probeer deze dan te inflaten. Het gaat wel goed als ik het bestand open als een door middel van gzopen en dan inflate met gzread/gzgets. De code die ik gebruik staat hier onder en zegt waarschijnlijk meer dan dit stukje tekst.

Wat extra info, was niet de bedoeling om een indruk te wekken dat ik er zelf nog niet naar gekeken had en het hier maat neer donder. Zlib versie is 1.2.3, VS2005 standaart debug compile.

Ik heb google en got al afgezocht en niks kunnen vinden dat relevant is voor dir probleem. De string is namelijk wel gzip ok en als ik hem dus wegschrijf naar een file en daarna open met gz_open doen zich bij inflate geen problemen op. Weg schrijfen naar een file is natuurlijk niet echt handig omdat de data al in het gehuegen zit.

Ik heb ook al de gz_open code van zlib bestudeert maar die leest het bestand ook gewoon in en naar een z_stream. Ik hoop dus dat iemand me wat kan helpen hier mee, misschien vergeet ik iets belangrijks. Zelf staar ik me al uren dood op de code :'( .

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//log
debug("Handle gzip data.");

// decompress buffer
std::string data;
Bytef *rdata  = new Bytef[2048];
uLongf *rsize = new uLongf(2048);

std::string buffer2,buffer4,out;

// prepare for file read
char c;
FILE *df = fopen ( "./data.gz" , "rb" );

// read file
fread(&c,1,1,df);
while (!feof(df))
{
    buffer4 += c;
    fread(&c,1,1,df);
}

/*
 * Het resultaat van de file is gelijk aan wat er in het geheugen staat
 * zlib_error print textueel de error naar de console
 * De util::decompress is een eigen layer over de streams van zlib heen.
 */

// inflate trough custom layer - result is bad
util::zlib_error( 
    util::decompress(buffer4,out) 
);

/*
 * uncompress methode zit standaart in zlib. Dit is ook een layer over de streams heen.
 * GetContentPtr() is de lenghte in bytes van de ge compressde data
 * rdata is het doel, en rsize de groote van het doel. 
 * Naar decompressie wordt rsize de grote van het decompress resultaat (rdata dus).
 * Buffer4 is een std::string die gecast wordt naar een 
 * Bytef (Byte far) die gedefined wordt in zlib.h
 * 
 * buffer4.size()+1 is ook al geprobeert maar werkt helaas ook niet.
 */

// inflate trough zlib api layer - result is bad
util::zlib_error( 
    uncompress(rdata,rsize,(Bytef*)buffer4.c_str(),buffer4.size()) 
);

// try decompress file - result is good
char *buffer3 = new char[2048];
gzFile fg = gzopen( "./data.gz", "rb" );
gzgets( fg, buffer3, 2048);
std::string fd(buffer3);

[ Voor 56% gewijzigd door codeneos op 06-12-2006 20:09 ]

http://www.tweakers.net/gallery/119170/sys


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
En nu mogen wij je code debuggen? Of heb je dat zelf al gedaan? En wat was daaruit je conclusie?
Wat heb je al geprobeerd? Heb je al gezocht? Iets gevonden? Waar gaat het precies fout, wanneer treedt die error op? Kijk even in de Programming Beleid Quickstart voor informatie over hoe je een goed topic opent in PRG ;)

Ik laat 'm nog even staan als je 'm aanvult met extra informatie. Het is in PRG niet de bedoeling om je probleem neer te plempen en te wachten op een oplossing.

Vermeld overigens ook even wat versienummers e.d. waar van toepassing.

[ Voor 78% gewijzigd door RobIII op 06-12-2006 19:43 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Wat een... erm, aparte manier om een bestand in te lezen.

Verder initialiseer je maar een deel van je buffer, maar geef je wel 2048 als grootte van je buffer mee (als ik het goed begrepen heb). Dat is natuurlijk ook vragen om problemen.

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Ik maak een buffer van 2048 en geef hem een grote van 2048 mee. Ik zie daar geen problemen, de buffer is toch gewoon ge-init?

[ Voor 110% gewijzigd door codeneos op 06-12-2006 19:58 ]

http://www.tweakers.net/gallery/119170/sys


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Ah, dat is je destination buffer... ok, dat klopt, maar wat doet die GetContentPtr() functie dan precies? (Die zou de grootte van de source buffer moeten opleveren, maar ik heb geen idee hoe 'ie dat zou kunnen doen aangezien 'ie geen referentie heeft naar buffer4).

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
GetContentPtr() geeft inderdaad de groote van buffer4, is wat onduidelijk omdat er nog een stuk code boven zit waarin in eerst het resultaat van een methode weg schrijf naar een file (om te testen) en dan weer lees. Dat staat er in omdat ik het probleem dus probeerde te vinden ;). Ik heb het nu vervangen door buffer4.size() maar voor de werking van het programma maakt het niet uit :'(.

http://www.tweakers.net/gallery/119170/sys


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
't Helpt ons vast als je in zlib opzoekt wat error code -3 is.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Error in de deflate stream (incomplete or invalid). Had het er inderdaad beter even bij kunnen vermelden ;).

De ander modelijk error codes zijn:
code:
1
2
3
4
5
6
7
8
9
#define Z_OK            0
#define Z_STREAM_END    1
#define Z_NEED_DICT     2
#define Z_ERRNO        (-1)
#define Z_STREAM_ERROR (-2)
#define Z_DATA_ERROR   (-3)
#define Z_MEM_ERROR    (-4)
#define Z_BUF_ERROR    (-5)
#define Z_VERSION_ERROR (-6)

[ Voor 82% gewijzigd door codeneos op 06-12-2006 20:22 ]

http://www.tweakers.net/gallery/119170/sys


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Is het trouwens niet zo dat uncompress() alleen data in 'deflate' formaat decodeert, en dat de gz-functies een bestand in 'gzip' formaat decoderen, waarbij gzip een containerformaat is? In dat geval is het logisch dat de letterlijke inhoud van een gzip-bestand geen geldige deflate-data bevat.

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Heb ik ook al aangedacht. Maar er is geen methode de gunzip heet dus hoe zou ik anders deze data moeten uncompressen? Ik heb geen idee hoelang de gz header is en of dit verschild per file. Gzopen doe wel iets met de header.

[ Voor 31% gewijzigd door codeneos op 06-12-2006 20:45 ]

http://www.tweakers.net/gallery/119170/sys


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Lees ze:
RFC 1952: GZIP file format specification

Het is geen heel complex bestandsformaat. Alle header-data zit vóór de deflated data, maar je moet wel een aantal optionele headers overslaan, en er rekening mee houden dat een enkel bestand uit meerdere datablokken kan bestaan.

Waarom wil je trouwens die gz-functies niet gebruiken? Die doen dit werk juist al voor je.

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Bedankt, het is inderdaad best lastig. De data komt compressed binnen over het netwerk met de gzip header. Nu wil ik niet eerste van data weer een bestand maken en dan opne met gz_open om het daarna weer direct te verwijderen.

Er zitten ook functies in zlib die de header uit een bestand halen of igg overslaan (checkheader()). Dus misschien kan ik een aanpassing hier op doen zat dat het de header direct uit een char * weg haalt.

http://www.tweakers.net/gallery/119170/sys


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Een deflate-stream is gelukkig self-terminating. Verder is er in de zlib source een prima voorbeeld te vinden om de gzip-header over te slaan:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* skip over gzip header */
local void gzheader(file *in)
{
    int flags;
    unsigned n;

    if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
    if (read1(in) != 8) bye("unknown compression method in", in->name);
    flags = read1(in);
    if (flags & 0xe0) bye("unknown header flags set in", in->name);
    skip(in, 6);
    if (flags & 4) {
        n = read1(in);
        n += (unsigned)(read1(in)) << 8;
        skip(in, n);
    }
    if (flags & 8) while (read1(in) != 0) ;
    if (flags & 16) while (read1(in) != 0) ;
    if (flags & 2) skip(in, 2);
}

Deze code zegt eigenlijk alles (en komt ook overeen met de RFC). Als je het bestand al in je geheugen hebt, is het aanpassen van read1()/skip() triviaal (wel oppassen dat je niet buiten je buffer leest!).

Het is dan dus een kwestie van de header overslaan (en afbreken als die ongeldig is), en daarna met zlib de deflate-data decoderen.

[ Voor 6% gewijzigd door Soultaker op 06-12-2006 21:17 ]


  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Bedankt, zoiets had ik zelf ook al. Inplaats van de code uit zlib probeer ik de eerste 10 bytes over te slaan. Dit is net wel even iets beter, in mijn geval bestaat de header alleen uit de 2 gzip magic bytes (1F 8B) en de os code op het eind.

Als het allemaal nog niet lukt moet ik toch een temp filetje gebruiken :'(. Maar ik denk dat ik er nu wel uit kom!

edit:

Zelf was ik deze tegen gekomen maar die jij hebt gepost is een stuk cleaner.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/* ===========================================================================
      Check the gzip header of a gz_stream opened for reading. Set the stream
    mode to transparent if the gzip magic header is not present; set s->err
    to Z_DATA_ERROR if the magic header is present but the rest of the header
    is incorrect.
    IN assertion: the stream s has already been created sucessfully;
       s->stream.avail_in is zero for the first time, but may be non-zero
       for concatenated .gz files.
*/
local void check_header(s)
    gz_stream *s;
{
    int method; /* method byte */
    int flags;  /* flags byte */
    uInt len;
    int c;

    /* Assure two bytes in the buffer so we can peek ahead -- handle case
       where first byte of header is at the end of the buffer after the last
       gzip segment */
    len = s->stream.avail_in;
    if (len < 2) {
        if (len) s->inbuf[0] = s->stream.next_in[0];
        errno = 0;
        len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
        if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
        s->stream.avail_in += len;
        s->stream.next_in = s->inbuf;
        if (s->stream.avail_in < 2) {
            s->transparent = s->stream.avail_in;
            return;
        }
    }

    /* Peek ahead to check the gzip magic header */
    if (s->stream.next_in[0] != gz_magic[0] ||
        s->stream.next_in[1] != gz_magic[1]) {
        s->transparent = 1;
        return;
    }
    s->stream.avail_in -= 2;
    s->stream.next_in += 2;

    /* Check the rest of the gzip header */
    method = get_byte(s);
    flags = get_byte(s);
    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
        s->z_err = Z_DATA_ERROR;
        return;
    }

    /* Discard time, xflags and OS code: */
    for (len = 0; len < 6; len++) (void)get_byte(s);

    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
        len  =  (uInt)get_byte(s);
        len += ((uInt)get_byte(s))<<8;
        /* len is garbage if EOF but the loop below will quit anyway */
        while (len-- != 0 && get_byte(s) != EOF) ;
    }
    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
        while ((c = get_byte(s)) != 0 && c != EOF) ;
    }
    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
        while ((c = get_byte(s)) != 0 && c != EOF) ;
    }
    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
        for (len = 0; len < 2; len++) (void)get_byte(s);
    }
    s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
}

[ Voor 79% gewijzigd door codeneos op 07-12-2006 13:54 ]

http://www.tweakers.net/gallery/119170/sys


  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 14:14
Ik heb nu een simpele methode om de header van de data weg te halen. Nu de header weg is werkt de uncompress helaas nog steeds niet. Ik ben 100% zeker dat ik alleen de header weg heb gehaald.

Ik post de code hier zodat anderen er misschien later ook wat aan hebben. De code is zo opgezet dat hij niet veel verschilt van de code die soultaker poste.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* reads a single byte from the string staring at ptr */
const unsigned char ReadByte( const std::string data, size_t &ptr )
{
    unsigned char res = 0;
    
    if( data.size() >= ptr ) 
        res = (unsigned char)data[ptr];

    ptr += 1;
    return res;
}

/* reads a single byte from the string staring at ptr */
void SkipByte( const std::string data, size_t skip, size_t &ptr )
{
    ptr += skip;
}

/* skip over gzip header */
void SkipGzHeader(std::string &str)
{
    int flags;
    size_t loc  = 0;
    unsigned n;

    if (ReadByte(str,loc) != 31 || ReadByte(str,loc) != 139) printf("String not a gzip format");
    if (ReadByte(str,loc) != 8) printf("Unknown compression method in string");
    flags = ReadByte(str,loc);
    if (flags & 0xe0) printf("Unknown header flags set in string");
    SkipByte(str, 6, loc);
    if (flags & 4) {
        n = ReadByte(str,loc);
        n += (unsigned)(ReadByte(str,loc)) << 8;
        SkipByte(str, n, loc);
    }
    if (flags & 8) while (ReadByte(str,loc) != 0) ;
    if (flags & 16) while (ReadByte(str,loc) != 0) ;
    if (flags & 2) SkipByte(str, 2, loc);

    str.erase( 0 , loc );
}


Edit: Ik kwam deze code toevallig tegen, deze lost het probleem op: http://www.codeguru.com/c...ession/article.php/c5125/ . Het is een gziphelper van codeguru uit een article van 2003.

[ Voor 7% gewijzigd door codeneos op 07-12-2006 14:29 ]

http://www.tweakers.net/gallery/119170/sys


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 14:58
Om het probleem te vinden is toch wat meer debug-info nodig. Kun je misschien een hexadecimale representatie vinden van wat er in je string zit, en aangeven waar 'loc' op uitkomt?

(Printen kan bijvoorbeeld zo:)
C++:
1
2
3
const char *hexdigits = "0123456789ABCDEF";
for(std::string::const_iterator i = str.begin(); i != str.end(); ++i)
    cout << hexdigits[((*i) >> 4)&15] << hexdigits[(*i)&15] << ' ';                


Verder zie ik nog twee foutjes:
• Op regel 6 moet je > i.p.v. >= gebruiken; het laatste geldige karakter is data[data.size() - 1].
• Als je strings niet wijzigt, kun je ze het beste 'by const reference' doorgeven, zodat ze niet steeds gekopieerd hoeven worden (heel inefficiënt, als je het zo vaak doet als hier!):
C++:
1
2
3
const unsigned char ReadByte( const std::string &data, size_t &ptr )
..
void SkipByte( const std::string &data, size_t skip, size_t &ptr ) 

(Let op de extra '&'.)
Helaas is geen van beide is de oorzaak van je probleem, voor zover ik kan zien.

[ Voor 4% gewijzigd door Soultaker op 07-12-2006 15:14 ]

Pagina: 1